aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/xen-acpi-cpuhotplug.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-02-24 19:06:13 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-24 19:18:31 -0500
commit77be36de8b07027a70fbc8f02703ccd76cd16d09 (patch)
treee2310718e39cb0dcfe93942d4c990df9154a7ecd /drivers/xen/xen-acpi-cpuhotplug.c
parent89f883372fa60f604d136924baf3e89ff1870e9e (diff)
parentc81611c4e96f595a80d8be9367c385d2c116428b (diff)
Merge tag 'stable/for-linus-3.9-rc0-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/konrad/xen
Pull Xen update from Konrad Rzeszutek Wilk: "This has two new ACPI drivers for Xen - a physical CPU offline/online and a memory hotplug. The way this works is that ACPI kicks the drivers and they make the appropiate hypercall to the hypervisor to tell it that there is a new CPU or memory. There also some changes to the Xen ARM ABIs and couple of fixes. One particularly nasty bug in the Xen PV spinlock code was fixed by Stefan Bader - and has been there since the 2.6.32! Features: - Xen ACPI memory and CPU hotplug drivers - allowing Xen hypervisor to be aware of new CPU and new DIMMs - Cleanups Bug-fixes: - Fixes a long-standing bug in the PV spinlock wherein we did not kick VCPUs that were in a tight loop. - Fixes in the error paths for the event channel machinery" Fix up a few semantic conflicts with the ACPI interface changes in drivers/xen/xen-acpi-{cpu,mem}hotplug.c. * tag 'stable/for-linus-3.9-rc0-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/konrad/xen: xen: event channel arrays are xen_ulong_t and not unsigned long xen: Send spinlock IPI to all waiters xen: introduce xen_remap, use it instead of ioremap xen: close evtchn port if binding to irq fails xen-evtchn: correct comment and error output xen/tmem: Add missing %s in the printk statement. xen/acpi: move xen_acpi_get_pxm under CONFIG_XEN_DOM0 xen/acpi: ACPI cpu hotplug xen/acpi: Move xen_acpi_get_pxm to Xen's acpi.h xen/stub: driver for CPU hotplug xen/acpi: ACPI memory hotplug xen/stub: driver for memory hotplug xen: implement updated XENMEM_add_to_physmap_range ABI xen/smp: Move the common CPU init code a bit to prep for PVH patch.
Diffstat (limited to 'drivers/xen/xen-acpi-cpuhotplug.c')
-rw-r--r--drivers/xen/xen-acpi-cpuhotplug.c471
1 files changed, 471 insertions, 0 deletions
diff --git a/drivers/xen/xen-acpi-cpuhotplug.c b/drivers/xen/xen-acpi-cpuhotplug.c
new file mode 100644
index 000000000000..757827966e34
--- /dev/null
+++ b/drivers/xen/xen-acpi-cpuhotplug.c
@@ -0,0 +1,471 @@
1/*
2 * Copyright (C) 2012 Intel Corporation
3 * Author: Liu Jinsong <jinsong.liu@intel.com>
4 * Author: Jiang Yunhong <yunhong.jiang@intel.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
14 * NON INFRINGEMENT. See the GNU General Public License for more
15 * details.
16 */
17
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/init.h>
21#include <linux/types.h>
22#include <linux/cpu.h>
23#include <linux/acpi.h>
24#include <linux/uaccess.h>
25#include <acpi/acpi_bus.h>
26#include <acpi/acpi_drivers.h>
27#include <acpi/processor.h>
28
29#include <xen/acpi.h>
30#include <xen/interface/platform.h>
31#include <asm/xen/hypercall.h>
32
33#define PREFIX "ACPI:xen_cpu_hotplug:"
34
35#define INSTALL_NOTIFY_HANDLER 0
36#define UNINSTALL_NOTIFY_HANDLER 1
37
38static acpi_status xen_acpi_cpu_hotadd(struct acpi_processor *pr);
39
40/* --------------------------------------------------------------------------
41 Driver Interface
42-------------------------------------------------------------------------- */
43
44static int xen_acpi_processor_enable(struct acpi_device *device)
45{
46 acpi_status status = 0;
47 unsigned long long value;
48 union acpi_object object = { 0 };
49 struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
50 struct acpi_processor *pr;
51
52 pr = acpi_driver_data(device);
53 if (!pr) {
54 pr_err(PREFIX "Cannot find driver data\n");
55 return -EINVAL;
56 }
57
58 if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) {
59 /* Declared with "Processor" statement; match ProcessorID */
60 status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer);
61 if (ACPI_FAILURE(status)) {
62 pr_err(PREFIX "Evaluating processor object\n");
63 return -ENODEV;
64 }
65
66 pr->acpi_id = object.processor.proc_id;
67 } else {
68 /* Declared with "Device" statement; match _UID */
69 status = acpi_evaluate_integer(pr->handle, METHOD_NAME__UID,
70 NULL, &value);
71 if (ACPI_FAILURE(status)) {
72 pr_err(PREFIX "Evaluating processor _UID\n");
73 return -ENODEV;
74 }
75
76 pr->acpi_id = value;
77 }
78
79 pr->id = xen_pcpu_id(pr->acpi_id);
80
81 if ((int)pr->id < 0)
82 /* This cpu is not presented at hypervisor, try to hotadd it */
83 if (ACPI_FAILURE(xen_acpi_cpu_hotadd(pr))) {
84 pr_err(PREFIX "Hotadd CPU (acpi_id = %d) failed.\n",
85 pr->acpi_id);
86 return -ENODEV;
87 }
88
89 return 0;
90}
91
92static int __cpuinit xen_acpi_processor_add(struct acpi_device *device)
93{
94 int ret;
95 struct acpi_processor *pr;
96
97 if (!device)
98 return -EINVAL;
99
100 pr = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL);
101 if (!pr)
102 return -ENOMEM;
103
104 pr->handle = device->handle;
105 strcpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME);
106 strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS);
107 device->driver_data = pr;
108
109 ret = xen_acpi_processor_enable(device);
110 if (ret)
111 pr_err(PREFIX "Error when enabling Xen processor\n");
112
113 return ret;
114}
115
116static int xen_acpi_processor_remove(struct acpi_device *device)
117{
118 struct acpi_processor *pr;
119
120 if (!device)
121 return -EINVAL;
122
123 pr = acpi_driver_data(device);
124 if (!pr)
125 return -EINVAL;
126
127 kfree(pr);
128 return 0;
129}
130
131/*--------------------------------------------------------------
132 Acpi processor hotplug support
133--------------------------------------------------------------*/
134
135static int is_processor_present(acpi_handle handle)
136{
137 acpi_status status;
138 unsigned long long sta = 0;
139
140
141 status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
142
143 if (ACPI_SUCCESS(status) && (sta & ACPI_STA_DEVICE_PRESENT))
144 return 1;
145
146 /*
147 * _STA is mandatory for a processor that supports hot plug
148 */
149 if (status == AE_NOT_FOUND)
150 pr_info(PREFIX "Processor does not support hot plug\n");
151 else
152 pr_info(PREFIX "Processor Device is not present");
153 return 0;
154}
155
156static int xen_apic_id(acpi_handle handle)
157{
158 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
159 union acpi_object *obj;
160 struct acpi_madt_local_apic *lapic;
161 int apic_id;
162
163 if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
164 return -EINVAL;
165
166 if (!buffer.length || !buffer.pointer)
167 return -EINVAL;
168
169 obj = buffer.pointer;
170 if (obj->type != ACPI_TYPE_BUFFER ||
171 obj->buffer.length < sizeof(*lapic)) {
172 kfree(buffer.pointer);
173 return -EINVAL;
174 }
175
176 lapic = (struct acpi_madt_local_apic *)obj->buffer.pointer;
177
178 if (lapic->header.type != ACPI_MADT_TYPE_LOCAL_APIC ||
179 !(lapic->lapic_flags & ACPI_MADT_ENABLED)) {
180 kfree(buffer.pointer);
181 return -EINVAL;
182 }
183
184 apic_id = (uint32_t)lapic->id;
185 kfree(buffer.pointer);
186 buffer.length = ACPI_ALLOCATE_BUFFER;
187 buffer.pointer = NULL;
188
189 return apic_id;
190}
191
192static int xen_hotadd_cpu(struct acpi_processor *pr)
193{
194 int cpu_id, apic_id, pxm;
195 struct xen_platform_op op;
196
197 apic_id = xen_apic_id(pr->handle);
198 if (apic_id < 0) {
199 pr_err(PREFIX "Failed to get apic_id for acpi_id %d\n",
200 pr->acpi_id);
201 return -ENODEV;
202 }
203
204 pxm = xen_acpi_get_pxm(pr->handle);
205 if (pxm < 0) {
206 pr_err(PREFIX "Failed to get _PXM for acpi_id %d\n",
207 pr->acpi_id);
208 return pxm;
209 }
210
211 op.cmd = XENPF_cpu_hotadd;
212 op.u.cpu_add.apic_id = apic_id;
213 op.u.cpu_add.acpi_id = pr->acpi_id;
214 op.u.cpu_add.pxm = pxm;
215
216 cpu_id = HYPERVISOR_dom0_op(&op);
217 if (cpu_id < 0)
218 pr_err(PREFIX "Failed to hotadd CPU for acpi_id %d\n",
219 pr->acpi_id);
220
221 return cpu_id;
222}
223
224static acpi_status xen_acpi_cpu_hotadd(struct acpi_processor *pr)
225{
226 if (!is_processor_present(pr->handle))
227 return AE_ERROR;
228
229 pr->id = xen_hotadd_cpu(pr);
230 if ((int)pr->id < 0)
231 return AE_ERROR;
232
233 /*
234 * Sync with Xen hypervisor, providing new /sys/.../xen_cpuX
235 * interface after cpu hotadded.
236 */
237 xen_pcpu_hotplug_sync();
238
239 return AE_OK;
240}
241
242static
243int acpi_processor_device_add(acpi_handle handle, struct acpi_device **device)
244{
245 acpi_handle phandle;
246 struct acpi_device *pdev;
247
248 if (acpi_get_parent(handle, &phandle))
249 return -ENODEV;
250
251 if (acpi_bus_get_device(phandle, &pdev))
252 return -ENODEV;
253
254 if (acpi_bus_scan(handle))
255 return -ENODEV;
256
257 return 0;
258}
259
260static int acpi_processor_device_remove(struct acpi_device *device)
261{
262 pr_debug(PREFIX "Xen does not support CPU hotremove\n");
263
264 return -ENOSYS;
265}
266
267static void acpi_processor_hotplug_notify(acpi_handle handle,
268 u32 event, void *data)
269{
270 struct acpi_processor *pr;
271 struct acpi_device *device = NULL;
272 u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
273 int result;
274
275 switch (event) {
276 case ACPI_NOTIFY_BUS_CHECK:
277 case ACPI_NOTIFY_DEVICE_CHECK:
278 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
279 "Processor driver received %s event\n",
280 (event == ACPI_NOTIFY_BUS_CHECK) ?
281 "ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK"));
282
283 if (!is_processor_present(handle))
284 break;
285
286 if (!acpi_bus_get_device(handle, &device))
287 break;
288
289 result = acpi_processor_device_add(handle, &device);
290 if (result) {
291 pr_err(PREFIX "Unable to add the device\n");
292 break;
293 }
294
295 ost_code = ACPI_OST_SC_SUCCESS;
296 break;
297
298 case ACPI_NOTIFY_EJECT_REQUEST:
299 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
300 "received ACPI_NOTIFY_EJECT_REQUEST\n"));
301
302 if (acpi_bus_get_device(handle, &device)) {
303 pr_err(PREFIX "Device don't exist, dropping EJECT\n");
304 break;
305 }
306 pr = acpi_driver_data(device);
307 if (!pr) {
308 pr_err(PREFIX "Driver data is NULL, dropping EJECT\n");
309 break;
310 }
311
312 /*
313 * TBD: implement acpi_processor_device_remove if Xen support
314 * CPU hotremove in the future.
315 */
316 acpi_processor_device_remove(device);
317 break;
318
319 default:
320 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
321 "Unsupported event [0x%x]\n", event));
322
323 /* non-hotplug event; possibly handled by other handler */
324 return;
325 }
326
327 (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL);
328 return;
329}
330
331static acpi_status is_processor_device(acpi_handle handle)
332{
333 struct acpi_device_info *info;
334 char *hid;
335 acpi_status status;
336
337 status = acpi_get_object_info(handle, &info);
338 if (ACPI_FAILURE(status))
339 return status;
340
341 if (info->type == ACPI_TYPE_PROCESSOR) {
342 kfree(info);
343 return AE_OK; /* found a processor object */
344 }
345
346 if (!(info->valid & ACPI_VALID_HID)) {
347 kfree(info);
348 return AE_ERROR;
349 }
350
351 hid = info->hardware_id.string;
352 if ((hid == NULL) || strcmp(hid, ACPI_PROCESSOR_DEVICE_HID)) {
353 kfree(info);
354 return AE_ERROR;
355 }
356
357 kfree(info);
358 return AE_OK; /* found a processor device object */
359}
360
361static acpi_status
362processor_walk_namespace_cb(acpi_handle handle,
363 u32 lvl, void *context, void **rv)
364{
365 acpi_status status;
366 int *action = context;
367
368 status = is_processor_device(handle);
369 if (ACPI_FAILURE(status))
370 return AE_OK; /* not a processor; continue to walk */
371
372 switch (*action) {
373 case INSTALL_NOTIFY_HANDLER:
374 acpi_install_notify_handler(handle,
375 ACPI_SYSTEM_NOTIFY,
376 acpi_processor_hotplug_notify,
377 NULL);
378 break;
379 case UNINSTALL_NOTIFY_HANDLER:
380 acpi_remove_notify_handler(handle,
381 ACPI_SYSTEM_NOTIFY,
382 acpi_processor_hotplug_notify);
383 break;
384 default:
385 break;
386 }
387
388 /* found a processor; skip walking underneath */
389 return AE_CTRL_DEPTH;
390}
391
392static
393void acpi_processor_install_hotplug_notify(void)
394{
395 int action = INSTALL_NOTIFY_HANDLER;
396 acpi_walk_namespace(ACPI_TYPE_ANY,
397 ACPI_ROOT_OBJECT,
398 ACPI_UINT32_MAX,
399 processor_walk_namespace_cb, NULL, &action, NULL);
400}
401
402static
403void acpi_processor_uninstall_hotplug_notify(void)
404{
405 int action = UNINSTALL_NOTIFY_HANDLER;
406 acpi_walk_namespace(ACPI_TYPE_ANY,
407 ACPI_ROOT_OBJECT,
408 ACPI_UINT32_MAX,
409 processor_walk_namespace_cb, NULL, &action, NULL);
410}
411
412static const struct acpi_device_id processor_device_ids[] = {
413 {ACPI_PROCESSOR_OBJECT_HID, 0},
414 {ACPI_PROCESSOR_DEVICE_HID, 0},
415 {"", 0},
416};
417MODULE_DEVICE_TABLE(acpi, processor_device_ids);
418
419static struct acpi_driver xen_acpi_processor_driver = {
420 .name = "processor",
421 .class = ACPI_PROCESSOR_CLASS,
422 .ids = processor_device_ids,
423 .ops = {
424 .add = xen_acpi_processor_add,
425 .remove = xen_acpi_processor_remove,
426 },
427};
428
429static int __init xen_acpi_processor_init(void)
430{
431 int result = 0;
432
433 if (!xen_initial_domain())
434 return -ENODEV;
435
436 /* unregister the stub which only used to reserve driver space */
437 xen_stub_processor_exit();
438
439 result = acpi_bus_register_driver(&xen_acpi_processor_driver);
440 if (result < 0) {
441 xen_stub_processor_init();
442 return result;
443 }
444
445 acpi_processor_install_hotplug_notify();
446 return 0;
447}
448
449static void __exit xen_acpi_processor_exit(void)
450{
451 if (!xen_initial_domain())
452 return;
453
454 acpi_processor_uninstall_hotplug_notify();
455
456 acpi_bus_unregister_driver(&xen_acpi_processor_driver);
457
458 /*
459 * stub reserve space again to prevent any chance of native
460 * driver loading.
461 */
462 xen_stub_processor_init();
463 return;
464}
465
466module_init(xen_acpi_processor_init);
467module_exit(xen_acpi_processor_exit);
468ACPI_MODULE_NAME("xen-acpi-cpuhotplug");
469MODULE_AUTHOR("Liu Jinsong <jinsong.liu@intel.com>");
470MODULE_DESCRIPTION("Xen Hotplug CPU Driver");
471MODULE_LICENSE("GPL");