diff options
Diffstat (limited to 'drivers/xen')
-rw-r--r-- | drivers/xen/Kconfig | 12 | ||||
-rw-r--r-- | drivers/xen/Makefile | 1 | ||||
-rw-r--r-- | drivers/xen/pcpu.c | 35 | ||||
-rw-r--r-- | drivers/xen/xen-acpi-cpuhotplug.c | 471 |
4 files changed, 519 insertions, 0 deletions
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index dcffad1b47ca..5a32232cf7c1 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig | |||
@@ -202,6 +202,18 @@ config XEN_ACPI_HOTPLUG_MEMORY | |||
202 | to hot-add memory at runtime (the hot-added memory cannot be | 202 | to hot-add memory at runtime (the hot-added memory cannot be |
203 | removed until machine stop), select Y/M here, otherwise select N. | 203 | removed until machine stop), select Y/M here, otherwise select N. |
204 | 204 | ||
205 | config XEN_ACPI_HOTPLUG_CPU | ||
206 | tristate "Xen ACPI cpu hotplug" | ||
207 | depends on XEN_DOM0 && XEN_STUB && ACPI | ||
208 | select ACPI_CONTAINER | ||
209 | default n | ||
210 | help | ||
211 | Xen ACPI cpu enumerating and hotplugging | ||
212 | |||
213 | For hotplugging, currently Xen only support ACPI cpu hotadd. | ||
214 | If you want to hotadd cpu at runtime (the hotadded cpu cannot | ||
215 | be removed until machine stop), select Y/M here. | ||
216 | |||
205 | config XEN_ACPI_PROCESSOR | 217 | config XEN_ACPI_PROCESSOR |
206 | tristate "Xen ACPI processor" | 218 | tristate "Xen ACPI processor" |
207 | depends on XEN && X86 && ACPI_PROCESSOR && CPU_FREQ | 219 | depends on XEN && X86 && ACPI_PROCESSOR && CPU_FREQ |
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 1605f594ff56..eabd0ee1c2bc 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile | |||
@@ -32,6 +32,7 @@ obj-$(CONFIG_XEN_PCIDEV_BACKEND) += xen-pciback/ | |||
32 | obj-$(CONFIG_XEN_PRIVCMD) += xen-privcmd.o | 32 | obj-$(CONFIG_XEN_PRIVCMD) += xen-privcmd.o |
33 | obj-$(CONFIG_XEN_STUB) += xen-stub.o | 33 | obj-$(CONFIG_XEN_STUB) += xen-stub.o |
34 | obj-$(CONFIG_XEN_ACPI_HOTPLUG_MEMORY) += xen-acpi-memhotplug.o | 34 | obj-$(CONFIG_XEN_ACPI_HOTPLUG_MEMORY) += xen-acpi-memhotplug.o |
35 | obj-$(CONFIG_XEN_ACPI_HOTPLUG_CPU) += xen-acpi-cpuhotplug.o | ||
35 | obj-$(CONFIG_XEN_ACPI_PROCESSOR) += xen-acpi-processor.o | 36 | obj-$(CONFIG_XEN_ACPI_PROCESSOR) += xen-acpi-processor.o |
36 | xen-evtchn-y := evtchn.o | 37 | xen-evtchn-y := evtchn.o |
37 | xen-gntdev-y := gntdev.o | 38 | xen-gntdev-y := gntdev.o |
diff --git a/drivers/xen/pcpu.c b/drivers/xen/pcpu.c index 067fcfa1723e..0836a18675b1 100644 --- a/drivers/xen/pcpu.c +++ b/drivers/xen/pcpu.c | |||
@@ -333,6 +333,41 @@ static irqreturn_t xen_pcpu_interrupt(int irq, void *dev_id) | |||
333 | return IRQ_HANDLED; | 333 | return IRQ_HANDLED; |
334 | } | 334 | } |
335 | 335 | ||
336 | /* Sync with Xen hypervisor after cpu hotadded */ | ||
337 | void xen_pcpu_hotplug_sync(void) | ||
338 | { | ||
339 | schedule_work(&xen_pcpu_work); | ||
340 | } | ||
341 | EXPORT_SYMBOL_GPL(xen_pcpu_hotplug_sync); | ||
342 | |||
343 | /* | ||
344 | * For hypervisor presented cpu, return logic cpu id; | ||
345 | * For hypervisor non-presented cpu, return -ENODEV. | ||
346 | */ | ||
347 | int xen_pcpu_id(uint32_t acpi_id) | ||
348 | { | ||
349 | int cpu_id = 0, max_id = 0; | ||
350 | struct xen_platform_op op; | ||
351 | |||
352 | op.cmd = XENPF_get_cpuinfo; | ||
353 | while (cpu_id <= max_id) { | ||
354 | op.u.pcpu_info.xen_cpuid = cpu_id; | ||
355 | if (HYPERVISOR_dom0_op(&op)) { | ||
356 | cpu_id++; | ||
357 | continue; | ||
358 | } | ||
359 | |||
360 | if (acpi_id == op.u.pcpu_info.acpi_id) | ||
361 | return cpu_id; | ||
362 | if (op.u.pcpu_info.max_present > max_id) | ||
363 | max_id = op.u.pcpu_info.max_present; | ||
364 | cpu_id++; | ||
365 | } | ||
366 | |||
367 | return -ENODEV; | ||
368 | } | ||
369 | EXPORT_SYMBOL_GPL(xen_pcpu_id); | ||
370 | |||
336 | static int __init xen_pcpu_init(void) | 371 | static int __init xen_pcpu_init(void) |
337 | { | 372 | { |
338 | int irq, ret; | 373 | int irq, ret; |
diff --git a/drivers/xen/xen-acpi-cpuhotplug.c b/drivers/xen/xen-acpi-cpuhotplug.c new file mode 100644 index 000000000000..9eefbb0f79a3 --- /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 | |||
38 | static acpi_status xen_acpi_cpu_hotadd(struct acpi_processor *pr); | ||
39 | |||
40 | /* -------------------------------------------------------------------------- | ||
41 | Driver Interface | ||
42 | -------------------------------------------------------------------------- */ | ||
43 | |||
44 | static 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 | |||
92 | static 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 | |||
116 | static int xen_acpi_processor_remove(struct acpi_device *device, int type) | ||
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 | |||
135 | static 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 | |||
156 | static 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 | |||
192 | static 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 | |||
224 | static 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 | |||
242 | static | ||
243 | int 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_add(device, pdev, handle, ACPI_BUS_TYPE_PROCESSOR)) | ||
255 | return -ENODEV; | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static 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 | |||
267 | static 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 | |||
331 | static 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 | |||
361 | static acpi_status | ||
362 | processor_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 | |||
392 | static | ||
393 | void 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 | |||
402 | static | ||
403 | void 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 | |||
412 | static const struct acpi_device_id processor_device_ids[] = { | ||
413 | {ACPI_PROCESSOR_OBJECT_HID, 0}, | ||
414 | {ACPI_PROCESSOR_DEVICE_HID, 0}, | ||
415 | {"", 0}, | ||
416 | }; | ||
417 | MODULE_DEVICE_TABLE(acpi, processor_device_ids); | ||
418 | |||
419 | static 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 | |||
429 | static 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 | |||
449 | static 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 | |||
466 | module_init(xen_acpi_processor_init); | ||
467 | module_exit(xen_acpi_processor_exit); | ||
468 | ACPI_MODULE_NAME("xen-acpi-cpuhotplug"); | ||
469 | MODULE_AUTHOR("Liu Jinsong <jinsong.liu@intel.com>"); | ||
470 | MODULE_DESCRIPTION("Xen Hotplug CPU Driver"); | ||
471 | MODULE_LICENSE("GPL"); | ||