Blame view

drivers/acpi/acpi_pad.c 12 KB
2025cf9e1   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
8e0af5141   Shaohua Li   ACPI: create Proc...
2
3
4
5
  /*
   * acpi_pad.c ACPI Processor Aggregator Driver
   *
   * Copyright (c) 2009, Intel Corporation.
8e0af5141   Shaohua Li   ACPI: create Proc...
6
7
8
9
10
11
12
13
   */
  
  #include <linux/kernel.h>
  #include <linux/cpumask.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/types.h>
  #include <linux/kthread.h>
ae7e81c07   Ingo Molnar   sched/headers: Pr...
14
  #include <uapi/linux/sched/types.h>
8e0af5141   Shaohua Li   ACPI: create Proc...
15
16
  #include <linux/freezer.h>
  #include <linux/cpu.h>
979081e74   Thomas Gleixner   ACPI/PAD: Use exp...
17
  #include <linux/tick.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
18
  #include <linux/slab.h>
8b48463f8   Lv Zheng   ACPI: Clean up in...
19
  #include <linux/acpi.h>
bc83cccc7   H. Peter Anvin   x86, mwait: Move ...
20
  #include <asm/mwait.h>
e311404f7   Juergen Gross   ACPI / PAD: don't...
21
  #include <xen/xen.h>
8e0af5141   Shaohua Li   ACPI: create Proc...
22

a40770a95   Dan Carpenter   acpi_pad: "proces...
23
  #define ACPI_PROCESSOR_AGGREGATOR_CLASS	"acpi_pad"
8e0af5141   Shaohua Li   ACPI: create Proc...
24
25
26
  #define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator"
  #define ACPI_PROCESSOR_AGGREGATOR_NOTIFY 0x80
  static DEFINE_MUTEX(isolated_cpus_lock);
5f1601261   Stuart Hayes   acpi_pad: fix pow...
27
  static DEFINE_MUTEX(round_robin_lock);
8e0af5141   Shaohua Li   ACPI: create Proc...
28

8e0af5141   Shaohua Li   ACPI: create Proc...
29
  static unsigned long power_saving_mwait_eax;
0dc698b93   Venkatesh Pallipadi   ACPI: Don't let a...
30
31
32
  
  static unsigned char tsc_detected_unstable;
  static unsigned char tsc_marked_unstable;
8e0af5141   Shaohua Li   ACPI: create Proc...
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  static void power_saving_mwait_init(void)
  {
  	unsigned int eax, ebx, ecx, edx;
  	unsigned int highest_cstate = 0;
  	unsigned int highest_subcstate = 0;
  	int i;
  
  	if (!boot_cpu_has(X86_FEATURE_MWAIT))
  		return;
  	if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF)
  		return;
  
  	cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx);
  
  	if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) ||
  	    !(ecx & CPUID5_ECX_INTERRUPT_BREAK))
  		return;
  
  	edx >>= MWAIT_SUBSTATE_SIZE;
  	for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) {
  		if (edx & MWAIT_SUBSTATE_MASK) {
  			highest_cstate = i;
  			highest_subcstate = edx & MWAIT_SUBSTATE_MASK;
  		}
  	}
  	power_saving_mwait_eax = (highest_cstate << MWAIT_SUBSTATE_SIZE) |
  		(highest_subcstate - 1);
592913ecb   John Stultz   time: Kill off CO...
60
  #if defined(CONFIG_X86)
8e0af5141   Shaohua Li   ACPI: create Proc...
61
  	switch (boot_cpu_data.x86_vendor) {
7377ed4bd   Pu Wen   ACPI: Add Hygon D...
62
  	case X86_VENDOR_HYGON:
8e0af5141   Shaohua Li   ACPI: create Proc...
63
64
  	case X86_VENDOR_AMD:
  	case X86_VENDOR_INTEL:
773b2f30a   Tony W Wang-oc   ACPI, x86: Add Zh...
65
  	case X86_VENDOR_ZHAOXIN:
8e0af5141   Shaohua Li   ACPI: create Proc...
66
67
68
69
  		/*
  		 * AMD Fam10h TSC will tick in all
  		 * C/P/S0/S1 states when this bit is set.
  		 */
8aa4b14eb   Chen Gong   ACPI: acpi_pad: D...
70
71
  		if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
  			tsc_detected_unstable = 1;
8aa4b14eb   Chen Gong   ACPI: acpi_pad: D...
72
  		break;
8e0af5141   Shaohua Li   ACPI: create Proc...
73
  	default:
3ff70551a   Thomas Gleixner   ACPI/PAD: Remove ...
74
  		/* TSC could halt in idle */
0dc698b93   Venkatesh Pallipadi   ACPI: Don't let a...
75
  		tsc_detected_unstable = 1;
8e0af5141   Shaohua Li   ACPI: create Proc...
76
77
78
79
80
81
82
83
84
85
86
87
  	}
  #endif
  }
  
  static unsigned long cpu_weight[NR_CPUS];
  static int tsk_in_cpu[NR_CPUS] = {[0 ... NR_CPUS-1] = -1};
  static DECLARE_BITMAP(pad_busy_cpus_bits, NR_CPUS);
  static void round_robin_cpu(unsigned int tsk_index)
  {
  	struct cpumask *pad_busy_cpus = to_cpumask(pad_busy_cpus_bits);
  	cpumask_var_t tmp;
  	int cpu;
f67538f81   Andrew Morton   acpi_pad: squish ...
88
  	unsigned long min_weight = -1;
134043cd6   Jason Yan   ACPI: PAD: Elimin...
89
  	unsigned long preferred_cpu;
8e0af5141   Shaohua Li   ACPI: create Proc...
90
91
92
  
  	if (!alloc_cpumask_var(&tmp, GFP_KERNEL))
  		return;
5f1601261   Stuart Hayes   acpi_pad: fix pow...
93
  	mutex_lock(&round_robin_lock);
8e0af5141   Shaohua Li   ACPI: create Proc...
94
95
  	cpumask_clear(tmp);
  	for_each_cpu(cpu, pad_busy_cpus)
06931e622   Bartosz Golaszewski   sched/topology: R...
96
  		cpumask_or(tmp, tmp, topology_sibling_cpumask(cpu));
8e0af5141   Shaohua Li   ACPI: create Proc...
97
98
99
100
101
  	cpumask_andnot(tmp, cpu_online_mask, tmp);
  	/* avoid HT sibilings if possible */
  	if (cpumask_empty(tmp))
  		cpumask_andnot(tmp, cpu_online_mask, pad_busy_cpus);
  	if (cpumask_empty(tmp)) {
5f1601261   Stuart Hayes   acpi_pad: fix pow...
102
  		mutex_unlock(&round_robin_lock);
8b29d29ab   Lenny Szubowicz   ACPI: acpi_pad: F...
103
  		free_cpumask_var(tmp);
8e0af5141   Shaohua Li   ACPI: create Proc...
104
105
106
107
108
109
110
111
112
113
114
115
116
117
  		return;
  	}
  	for_each_cpu(cpu, tmp) {
  		if (cpu_weight[cpu] < min_weight) {
  			min_weight = cpu_weight[cpu];
  			preferred_cpu = cpu;
  		}
  	}
  
  	if (tsk_in_cpu[tsk_index] != -1)
  		cpumask_clear_cpu(tsk_in_cpu[tsk_index], pad_busy_cpus);
  	tsk_in_cpu[tsk_index] = preferred_cpu;
  	cpumask_set_cpu(preferred_cpu, pad_busy_cpus);
  	cpu_weight[preferred_cpu]++;
5f1601261   Stuart Hayes   acpi_pad: fix pow...
118
  	mutex_unlock(&round_robin_lock);
8e0af5141   Shaohua Li   ACPI: create Proc...
119
120
  
  	set_cpus_allowed_ptr(current, cpumask_of(preferred_cpu));
8b29d29ab   Lenny Szubowicz   ACPI: acpi_pad: F...
121
122
  
  	free_cpumask_var(tmp);
8e0af5141   Shaohua Li   ACPI: create Proc...
123
124
125
126
127
128
129
130
131
132
  }
  
  static void exit_round_robin(unsigned int tsk_index)
  {
  	struct cpumask *pad_busy_cpus = to_cpumask(pad_busy_cpus_bits);
  	cpumask_clear_cpu(tsk_in_cpu[tsk_index], pad_busy_cpus);
  	tsk_in_cpu[tsk_index] = -1;
  }
  
  static unsigned int idle_pct = 5; /* percentage */
fa7584e13   Len Brown   ACPI: acpi_pad: t...
133
  static unsigned int round_robin_time = 1; /* second */
8e0af5141   Shaohua Li   ACPI: create Proc...
134
135
  static int power_saving_thread(void *data)
  {
8e0af5141   Shaohua Li   ACPI: create Proc...
136
137
138
  	int do_sleep;
  	unsigned int tsk_index = (unsigned long)data;
  	u64 last_jiffies = 0;
4ca6c1a06   Peter Zijlstra   sched,acpi_pad: C...
139
  	sched_set_fifo_low(current);
8e0af5141   Shaohua Li   ACPI: create Proc...
140
141
  
  	while (!kthread_should_stop()) {
4ff248f3b   Manuel Schölling   ACPI / PAD: Use t...
142
  		unsigned long expire_time;
8e0af5141   Shaohua Li   ACPI: create Proc...
143

8e0af5141   Shaohua Li   ACPI: create Proc...
144
  		/* round robin to cpus */
4ff248f3b   Manuel Schölling   ACPI / PAD: Use t...
145
146
  		expire_time = last_jiffies + round_robin_time * HZ;
  		if (time_before(expire_time, jiffies)) {
8e0af5141   Shaohua Li   ACPI: create Proc...
147
148
149
150
151
  			last_jiffies = jiffies;
  			round_robin_cpu(tsk_index);
  		}
  
  		do_sleep = 0;
8e0af5141   Shaohua Li   ACPI: create Proc...
152
153
154
  		expire_time = jiffies + HZ * (100 - idle_pct) / 100;
  
  		while (!need_resched()) {
0dc698b93   Venkatesh Pallipadi   ACPI: Don't let a...
155
156
157
158
159
  			if (tsc_detected_unstable && !tsc_marked_unstable) {
  				/* TSC could halt in idle, so notify users */
  				mark_tsc_unstable("TSC halts in idle");
  				tsc_marked_unstable = 1;
  			}
8e0af5141   Shaohua Li   ACPI: create Proc...
160
  			local_irq_disable();
979081e74   Thomas Gleixner   ACPI/PAD: Use exp...
161
  			tick_broadcast_enable();
c79521354   Thomas Gleixner   ACPI/PAD: Use exp...
162
  			tick_broadcast_enter();
8e0af5141   Shaohua Li   ACPI: create Proc...
163
  			stop_critical_timings();
168242553   Peter Zijlstra   x86, acpi, idle: ...
164
  			mwait_idle_with_hints(power_saving_mwait_eax, 1);
8e0af5141   Shaohua Li   ACPI: create Proc...
165
166
  
  			start_critical_timings();
c79521354   Thomas Gleixner   ACPI/PAD: Use exp...
167
  			tick_broadcast_exit();
8e0af5141   Shaohua Li   ACPI: create Proc...
168
  			local_irq_enable();
4ff248f3b   Manuel Schölling   ACPI / PAD: Use t...
169
  			if (time_before(expire_time, jiffies)) {
8e0af5141   Shaohua Li   ACPI: create Proc...
170
171
172
173
  				do_sleep = 1;
  				break;
  			}
  		}
8e0af5141   Shaohua Li   ACPI: create Proc...
174
175
176
177
178
179
180
  		/*
  		 * current sched_rt has threshold for rt task running time.
  		 * When a rt task uses 95% CPU time, the rt thread will be
  		 * scheduled out for 5% CPU time to not starve other tasks. But
  		 * the mechanism only works when all CPUs have RT task running,
  		 * as if one CPU hasn't RT task, RT task from other CPUs will
  		 * borrow CPU time from this CPU and cause RT task use > 95%
3b8cb427e   Chen Gong   acpi_pad: fix err...
181
  		 * CPU time. To make 'avoid starvation' work, takes a nap here.
8e0af5141   Shaohua Li   ACPI: create Proc...
182
  		 */
5b59c69ec   Tony Camuso   ACPI / PAD: call ...
183
  		if (unlikely(do_sleep))
8e0af5141   Shaohua Li   ACPI: create Proc...
184
  			schedule_timeout_killable(HZ * idle_pct / 100);
5b59c69ec   Tony Camuso   ACPI / PAD: call ...
185
186
187
188
189
190
191
  
  		/* If an external event has set the need_resched flag, then
  		 * we need to deal with it, or this loop will continue to
  		 * spin without calling __mwait().
  		 */
  		if (unlikely(need_resched()))
  			schedule();
8e0af5141   Shaohua Li   ACPI: create Proc...
192
193
194
195
196
197
198
199
200
201
  	}
  
  	exit_round_robin(tsk_index);
  	return 0;
  }
  
  static struct task_struct *ps_tsks[NR_CPUS];
  static unsigned int ps_tsk_num;
  static int create_power_saving_task(void)
  {
5d7e43862   Rusty Russell   acpi: Replace wei...
202
  	int rc;
3b8cb427e   Chen Gong   acpi_pad: fix err...
203

8e0af5141   Shaohua Li   ACPI: create Proc...
204
205
  	ps_tsks[ps_tsk_num] = kthread_run(power_saving_thread,
  		(void *)(unsigned long)ps_tsk_num,
150ed86fa   Len Brown   ACPI: acpi_pad: r...
206
  		"acpi_pad/%d", ps_tsk_num);
5d7e43862   Rusty Russell   acpi: Replace wei...
207
208
209
  
  	if (IS_ERR(ps_tsks[ps_tsk_num])) {
  		rc = PTR_ERR(ps_tsks[ps_tsk_num]);
3b8cb427e   Chen Gong   acpi_pad: fix err...
210
  		ps_tsks[ps_tsk_num] = NULL;
5d7e43862   Rusty Russell   acpi: Replace wei...
211
212
213
214
  	} else {
  		rc = 0;
  		ps_tsk_num++;
  	}
3b8cb427e   Chen Gong   acpi_pad: fix err...
215
216
  
  	return rc;
8e0af5141   Shaohua Li   ACPI: create Proc...
217
218
219
220
221
222
223
  }
  
  static void destroy_power_saving_task(void)
  {
  	if (ps_tsk_num > 0) {
  		ps_tsk_num--;
  		kthread_stop(ps_tsks[ps_tsk_num]);
3b8cb427e   Chen Gong   acpi_pad: fix err...
224
  		ps_tsks[ps_tsk_num] = NULL;
8e0af5141   Shaohua Li   ACPI: create Proc...
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
  	}
  }
  
  static void set_power_saving_task_num(unsigned int num)
  {
  	if (num > ps_tsk_num) {
  		while (ps_tsk_num < num) {
  			if (create_power_saving_task())
  				return;
  		}
  	} else if (num < ps_tsk_num) {
  		while (ps_tsk_num > num)
  			destroy_power_saving_task();
  	}
  }
3b8cb427e   Chen Gong   acpi_pad: fix err...
240
  static void acpi_pad_idle_cpus(unsigned int num_cpus)
8e0af5141   Shaohua Li   ACPI: create Proc...
241
242
243
244
245
246
247
  {
  	get_online_cpus();
  
  	num_cpus = min_t(unsigned int, num_cpus, num_online_cpus());
  	set_power_saving_task_num(num_cpus);
  
  	put_online_cpus();
8e0af5141   Shaohua Li   ACPI: create Proc...
248
249
250
251
252
253
254
255
256
257
258
  }
  
  static uint32_t acpi_pad_idle_cpus_num(void)
  {
  	return ps_tsk_num;
  }
  
  static ssize_t acpi_pad_rrtime_store(struct device *dev,
  	struct device_attribute *attr, const char *buf, size_t count)
  {
  	unsigned long num;
73d4511a5   Josh   ACPI: strict_strt...
259
  	if (kstrtoul(buf, 0, &num))
8e0af5141   Shaohua Li   ACPI: create Proc...
260
261
262
263
264
265
266
267
268
269
270
271
  		return -EINVAL;
  	if (num < 1 || num >= 100)
  		return -EINVAL;
  	mutex_lock(&isolated_cpus_lock);
  	round_robin_time = num;
  	mutex_unlock(&isolated_cpus_lock);
  	return count;
  }
  
  static ssize_t acpi_pad_rrtime_show(struct device *dev,
  	struct device_attribute *attr, char *buf)
  {
32297abd9   Naga Chumbalkar   ACPI: make acpi_p...
272
273
  	return scnprintf(buf, PAGE_SIZE, "%d
  ", round_robin_time);
8e0af5141   Shaohua Li   ACPI: create Proc...
274
275
276
277
278
279
280
281
282
  }
  static DEVICE_ATTR(rrtime, S_IRUGO|S_IWUSR,
  	acpi_pad_rrtime_show,
  	acpi_pad_rrtime_store);
  
  static ssize_t acpi_pad_idlepct_store(struct device *dev,
  	struct device_attribute *attr, const char *buf, size_t count)
  {
  	unsigned long num;
73d4511a5   Josh   ACPI: strict_strt...
283
  	if (kstrtoul(buf, 0, &num))
8e0af5141   Shaohua Li   ACPI: create Proc...
284
285
286
287
288
289
290
291
292
293
294
295
  		return -EINVAL;
  	if (num < 1 || num >= 100)
  		return -EINVAL;
  	mutex_lock(&isolated_cpus_lock);
  	idle_pct = num;
  	mutex_unlock(&isolated_cpus_lock);
  	return count;
  }
  
  static ssize_t acpi_pad_idlepct_show(struct device *dev,
  	struct device_attribute *attr, char *buf)
  {
32297abd9   Naga Chumbalkar   ACPI: make acpi_p...
296
297
  	return scnprintf(buf, PAGE_SIZE, "%d
  ", idle_pct);
8e0af5141   Shaohua Li   ACPI: create Proc...
298
299
300
301
302
303
304
305
306
  }
  static DEVICE_ATTR(idlepct, S_IRUGO|S_IWUSR,
  	acpi_pad_idlepct_show,
  	acpi_pad_idlepct_store);
  
  static ssize_t acpi_pad_idlecpus_store(struct device *dev,
  	struct device_attribute *attr, const char *buf, size_t count)
  {
  	unsigned long num;
73d4511a5   Josh   ACPI: strict_strt...
307
  	if (kstrtoul(buf, 0, &num))
8e0af5141   Shaohua Li   ACPI: create Proc...
308
309
310
311
312
313
314
315
316
317
  		return -EINVAL;
  	mutex_lock(&isolated_cpus_lock);
  	acpi_pad_idle_cpus(num);
  	mutex_unlock(&isolated_cpus_lock);
  	return count;
  }
  
  static ssize_t acpi_pad_idlecpus_show(struct device *dev,
  	struct device_attribute *attr, char *buf)
  {
5aaba3631   Sudeep Holla   cpumask: factor o...
318
319
  	return cpumap_print_to_pagebuf(false, buf,
  				       to_cpumask(pad_busy_cpus_bits));
8e0af5141   Shaohua Li   ACPI: create Proc...
320
  }
5aaba3631   Sudeep Holla   cpumask: factor o...
321

8e0af5141   Shaohua Li   ACPI: create Proc...
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
  static DEVICE_ATTR(idlecpus, S_IRUGO|S_IWUSR,
  	acpi_pad_idlecpus_show,
  	acpi_pad_idlecpus_store);
  
  static int acpi_pad_add_sysfs(struct acpi_device *device)
  {
  	int result;
  
  	result = device_create_file(&device->dev, &dev_attr_idlecpus);
  	if (result)
  		return -ENODEV;
  	result = device_create_file(&device->dev, &dev_attr_idlepct);
  	if (result) {
  		device_remove_file(&device->dev, &dev_attr_idlecpus);
  		return -ENODEV;
  	}
  	result = device_create_file(&device->dev, &dev_attr_rrtime);
  	if (result) {
  		device_remove_file(&device->dev, &dev_attr_idlecpus);
  		device_remove_file(&device->dev, &dev_attr_idlepct);
  		return -ENODEV;
  	}
  	return 0;
  }
  
  static void acpi_pad_remove_sysfs(struct acpi_device *device)
  {
  	device_remove_file(&device->dev, &dev_attr_idlecpus);
  	device_remove_file(&device->dev, &dev_attr_idlepct);
  	device_remove_file(&device->dev, &dev_attr_rrtime);
  }
c9ad8e062   Len Brown   ACPI: acpi_pad: s...
353
354
355
356
357
  /*
   * Query firmware how many CPUs should be idle
   * return -1 on failure
   */
  static int acpi_pad_pur(acpi_handle handle)
8e0af5141   Shaohua Li   ACPI: create Proc...
358
359
  {
  	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
8e0af5141   Shaohua Li   ACPI: create Proc...
360
  	union acpi_object *package;
c9ad8e062   Len Brown   ACPI: acpi_pad: s...
361
  	int num = -1;
8e0af5141   Shaohua Li   ACPI: create Proc...
362

3b8cb427e   Chen Gong   acpi_pad: fix err...
363
  	if (ACPI_FAILURE(acpi_evaluate_object(handle, "_PUR", NULL, &buffer)))
c9ad8e062   Len Brown   ACPI: acpi_pad: s...
364
  		return num;
3b8cb427e   Chen Gong   acpi_pad: fix err...
365
366
  
  	if (!buffer.length || !buffer.pointer)
c9ad8e062   Len Brown   ACPI: acpi_pad: s...
367
  		return num;
3b8cb427e   Chen Gong   acpi_pad: fix err...
368

8e0af5141   Shaohua Li   ACPI: create Proc...
369
  	package = buffer.pointer;
c9ad8e062   Len Brown   ACPI: acpi_pad: s...
370
371
372
373
374
375
  
  	if (package->type == ACPI_TYPE_PACKAGE &&
  		package->package.count == 2 &&
  		package->package.elements[0].integer.value == 1) /* rev 1 */
  
  		num = package->package.elements[1].integer.value;
8e0af5141   Shaohua Li   ACPI: create Proc...
376
  	kfree(buffer.pointer);
c9ad8e062   Len Brown   ACPI: acpi_pad: s...
377
  	return num;
8e0af5141   Shaohua Li   ACPI: create Proc...
378
  }
8e0af5141   Shaohua Li   ACPI: create Proc...
379
380
  static void acpi_pad_handle_notify(acpi_handle handle)
  {
3b8cb427e   Chen Gong   acpi_pad: fix err...
381
  	int num_cpus;
8e0af5141   Shaohua Li   ACPI: create Proc...
382
  	uint32_t idle_cpus;
8b296d941   Jiang Liu   ACPI / PAD: use a...
383
384
385
386
  	struct acpi_buffer param = {
  		.length = 4,
  		.pointer = (void *)&idle_cpus,
  	};
8e0af5141   Shaohua Li   ACPI: create Proc...
387
388
  
  	mutex_lock(&isolated_cpus_lock);
c9ad8e062   Len Brown   ACPI: acpi_pad: s...
389
390
  	num_cpus = acpi_pad_pur(handle);
  	if (num_cpus < 0) {
8e0af5141   Shaohua Li   ACPI: create Proc...
391
392
393
  		mutex_unlock(&isolated_cpus_lock);
  		return;
  	}
3b8cb427e   Chen Gong   acpi_pad: fix err...
394
  	acpi_pad_idle_cpus(num_cpus);
8e0af5141   Shaohua Li   ACPI: create Proc...
395
  	idle_cpus = acpi_pad_idle_cpus_num();
8b296d941   Jiang Liu   ACPI / PAD: use a...
396
  	acpi_evaluate_ost(handle, ACPI_PROCESSOR_AGGREGATOR_NOTIFY, 0, &param);
8e0af5141   Shaohua Li   ACPI: create Proc...
397
398
399
400
401
402
403
404
405
406
407
  	mutex_unlock(&isolated_cpus_lock);
  }
  
  static void acpi_pad_notify(acpi_handle handle, u32 event,
  	void *data)
  {
  	struct acpi_device *device = data;
  
  	switch (event) {
  	case ACPI_PROCESSOR_AGGREGATOR_NOTIFY:
  		acpi_pad_handle_notify(handle);
8e0af5141   Shaohua Li   ACPI: create Proc...
408
409
410
411
  		acpi_bus_generate_netlink_event(device->pnp.device_class,
  			dev_name(&device->dev), event, 0);
  		break;
  	default:
73d4511a5   Josh   ACPI: strict_strt...
412
413
  		pr_warn("Unsupported event [0x%x]
  ", event);
8e0af5141   Shaohua Li   ACPI: create Proc...
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
  		break;
  	}
  }
  
  static int acpi_pad_add(struct acpi_device *device)
  {
  	acpi_status status;
  
  	strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME);
  	strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS);
  
  	if (acpi_pad_add_sysfs(device))
  		return -ENODEV;
  
  	status = acpi_install_notify_handler(device->handle,
  		ACPI_DEVICE_NOTIFY, acpi_pad_notify, device);
  	if (ACPI_FAILURE(status)) {
  		acpi_pad_remove_sysfs(device);
  		return -ENODEV;
  	}
  
  	return 0;
  }
51fac8388   Rafael J. Wysocki   ACPI: Remove usel...
437
  static int acpi_pad_remove(struct acpi_device *device)
8e0af5141   Shaohua Li   ACPI: create Proc...
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
  {
  	mutex_lock(&isolated_cpus_lock);
  	acpi_pad_idle_cpus(0);
  	mutex_unlock(&isolated_cpus_lock);
  
  	acpi_remove_notify_handler(device->handle,
  		ACPI_DEVICE_NOTIFY, acpi_pad_notify);
  	acpi_pad_remove_sysfs(device);
  	return 0;
  }
  
  static const struct acpi_device_id pad_device_ids[] = {
  	{"ACPI000C", 0},
  	{"", 0},
  };
  MODULE_DEVICE_TABLE(acpi, pad_device_ids);
  
  static struct acpi_driver acpi_pad_driver = {
  	.name = "processor_aggregator",
  	.class = ACPI_PROCESSOR_AGGREGATOR_CLASS,
  	.ids = pad_device_ids,
  	.ops = {
  		.add = acpi_pad_add,
  		.remove = acpi_pad_remove,
  	},
  };
  
  static int __init acpi_pad_init(void)
  {
e311404f7   Juergen Gross   ACPI / PAD: don't...
467
468
469
  	/* Xen ACPI PAD is used when running as Xen Dom0. */
  	if (xen_initial_domain())
  		return -ENODEV;
8e0af5141   Shaohua Li   ACPI: create Proc...
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
  	power_saving_mwait_init();
  	if (power_saving_mwait_eax == 0)
  		return -EINVAL;
  
  	return acpi_bus_register_driver(&acpi_pad_driver);
  }
  
  static void __exit acpi_pad_exit(void)
  {
  	acpi_bus_unregister_driver(&acpi_pad_driver);
  }
  
  module_init(acpi_pad_init);
  module_exit(acpi_pad_exit);
  MODULE_AUTHOR("Shaohua Li<shaohua.li@intel.com>");
  MODULE_DESCRIPTION("ACPI Processor Aggregator Driver");
  MODULE_LICENSE("GPL");