aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen
diff options
context:
space:
mode:
authorLiu Jinsong <jinsong.liu@intel.com>2013-01-24 07:19:47 -0500
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2013-02-19 22:02:26 -0500
commitef92e7caf9901884f19fdeb4d7a24333b33c5f37 (patch)
tree3fc55f9288c42a08fa3e8ab27b26c78bd4d34f8b /drivers/xen
parentdcb93b96cec723783a81e8cac7df62feaf964792 (diff)
xen/acpi: ACPI memory hotplug
This patch implements real Xen acpi memory hotplug driver as module. When loaded, it replaces Xen stub driver. When an acpi memory device hotadd event occurs, it notifies OS and invokes notification callback, adding related memory device and parsing memory information, finally hypercall to xen hypervisor to add memory. Signed-off-by: Liu Jinsong <jinsong.liu@intel.com> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Diffstat (limited to 'drivers/xen')
-rw-r--r--drivers/xen/Kconfig11
-rw-r--r--drivers/xen/Makefile1
-rw-r--r--drivers/xen/xen-acpi-memhotplug.c501
3 files changed, 513 insertions, 0 deletions
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index c1c8566c6840..dcffad1b47ca 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -191,6 +191,17 @@ config XEN_STUB
191 191
192 To enable Xen features like cpu and memory hotplug, select Y here. 192 To enable Xen features like cpu and memory hotplug, select Y here.
193 193
194config XEN_ACPI_HOTPLUG_MEMORY
195 tristate "Xen ACPI memory hotplug"
196 depends on XEN_DOM0 && XEN_STUB && ACPI
197 default n
198 help
199 This is Xen ACPI memory hotplug.
200
201 Currently Xen only support ACPI memory hot-add. If you want
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.
204
194config XEN_ACPI_PROCESSOR 205config XEN_ACPI_PROCESSOR
195 tristate "Xen ACPI processor" 206 tristate "Xen ACPI processor"
196 depends on XEN && X86 && ACPI_PROCESSOR && CPU_FREQ 207 depends on XEN && X86 && ACPI_PROCESSOR && CPU_FREQ
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index b63edd824ad4..1605f594ff56 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_XEN_MCE_LOG) += mcelog.o
31obj-$(CONFIG_XEN_PCIDEV_BACKEND) += xen-pciback/ 31obj-$(CONFIG_XEN_PCIDEV_BACKEND) += xen-pciback/
32obj-$(CONFIG_XEN_PRIVCMD) += xen-privcmd.o 32obj-$(CONFIG_XEN_PRIVCMD) += xen-privcmd.o
33obj-$(CONFIG_XEN_STUB) += xen-stub.o 33obj-$(CONFIG_XEN_STUB) += xen-stub.o
34obj-$(CONFIG_XEN_ACPI_HOTPLUG_MEMORY) += xen-acpi-memhotplug.o
34obj-$(CONFIG_XEN_ACPI_PROCESSOR) += xen-acpi-processor.o 35obj-$(CONFIG_XEN_ACPI_PROCESSOR) += xen-acpi-processor.o
35xen-evtchn-y := evtchn.o 36xen-evtchn-y := evtchn.o
36xen-gntdev-y := gntdev.o 37xen-gntdev-y := gntdev.o
diff --git a/drivers/xen/xen-acpi-memhotplug.c b/drivers/xen/xen-acpi-memhotplug.c
new file mode 100644
index 000000000000..2c3759ac10f0
--- /dev/null
+++ b/drivers/xen/xen-acpi-memhotplug.c
@@ -0,0 +1,501 @@
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/acpi.h>
23#include <acpi/acpi_drivers.h>
24#include <xen/acpi.h>
25#include <xen/interface/platform.h>
26#include <asm/xen/hypercall.h>
27
28#define PREFIX "ACPI:xen_memory_hotplug:"
29
30struct acpi_memory_info {
31 struct list_head list;
32 u64 start_addr; /* Memory Range start physical addr */
33 u64 length; /* Memory Range length */
34 unsigned short caching; /* memory cache attribute */
35 unsigned short write_protect; /* memory read/write attribute */
36 /* copied from buffer getting from _CRS */
37 unsigned int enabled:1;
38};
39
40struct acpi_memory_device {
41 struct acpi_device *device;
42 struct list_head res_list;
43};
44
45static bool acpi_hotmem_initialized __read_mostly;
46
47static int xen_hotadd_memory(int pxm, struct acpi_memory_info *info)
48{
49 int rc;
50 struct xen_platform_op op;
51
52 op.cmd = XENPF_mem_hotadd;
53 op.u.mem_add.spfn = info->start_addr >> PAGE_SHIFT;
54 op.u.mem_add.epfn = (info->start_addr + info->length) >> PAGE_SHIFT;
55 op.u.mem_add.pxm = pxm;
56
57 rc = HYPERVISOR_dom0_op(&op);
58 if (rc)
59 pr_err(PREFIX "Xen Hotplug Memory Add failed on "
60 "0x%lx -> 0x%lx, _PXM: %d, error: %d\n",
61 (unsigned long)info->start_addr,
62 (unsigned long)(info->start_addr + info->length),
63 pxm, rc);
64
65 return rc;
66}
67
68static int xen_acpi_get_pxm(acpi_handle h)
69{
70 unsigned long long pxm;
71 acpi_status status;
72 acpi_handle handle;
73 acpi_handle phandle = h;
74
75 do {
76 handle = phandle;
77 status = acpi_evaluate_integer(handle, "_PXM", NULL, &pxm);
78 if (ACPI_SUCCESS(status))
79 return pxm;
80 status = acpi_get_parent(handle, &phandle);
81 } while (ACPI_SUCCESS(status));
82
83 return -ENXIO;
84}
85
86static int xen_acpi_memory_enable_device(struct acpi_memory_device *mem_device)
87{
88 int pxm, result;
89 int num_enabled = 0;
90 struct acpi_memory_info *info;
91
92 if (!mem_device)
93 return -EINVAL;
94
95 pxm = xen_acpi_get_pxm(mem_device->device->handle);
96 if (pxm < 0)
97 return pxm;
98
99 list_for_each_entry(info, &mem_device->res_list, list) {
100 if (info->enabled) { /* just sanity check...*/
101 num_enabled++;
102 continue;
103 }
104
105 if (!info->length)
106 continue;
107
108 result = xen_hotadd_memory(pxm, info);
109 if (result)
110 continue;
111 info->enabled = 1;
112 num_enabled++;
113 }
114
115 if (!num_enabled)
116 return -ENODEV;
117
118 return 0;
119}
120
121static acpi_status
122acpi_memory_get_resource(struct acpi_resource *resource, void *context)
123{
124 struct acpi_memory_device *mem_device = context;
125 struct acpi_resource_address64 address64;
126 struct acpi_memory_info *info, *new;
127 acpi_status status;
128
129 status = acpi_resource_to_address64(resource, &address64);
130 if (ACPI_FAILURE(status) ||
131 (address64.resource_type != ACPI_MEMORY_RANGE))
132 return AE_OK;
133
134 list_for_each_entry(info, &mem_device->res_list, list) {
135 if ((info->caching == address64.info.mem.caching) &&
136 (info->write_protect == address64.info.mem.write_protect) &&
137 (info->start_addr + info->length == address64.minimum)) {
138 info->length += address64.address_length;
139 return AE_OK;
140 }
141 }
142
143 new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL);
144 if (!new)
145 return AE_ERROR;
146
147 INIT_LIST_HEAD(&new->list);
148 new->caching = address64.info.mem.caching;
149 new->write_protect = address64.info.mem.write_protect;
150 new->start_addr = address64.minimum;
151 new->length = address64.address_length;
152 list_add_tail(&new->list, &mem_device->res_list);
153
154 return AE_OK;
155}
156
157static int
158acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
159{
160 acpi_status status;
161 struct acpi_memory_info *info, *n;
162
163 if (!list_empty(&mem_device->res_list))
164 return 0;
165
166 status = acpi_walk_resources(mem_device->device->handle,
167 METHOD_NAME__CRS, acpi_memory_get_resource, mem_device);
168
169 if (ACPI_FAILURE(status)) {
170 list_for_each_entry_safe(info, n, &mem_device->res_list, list)
171 kfree(info);
172 INIT_LIST_HEAD(&mem_device->res_list);
173 return -EINVAL;
174 }
175
176 return 0;
177}
178
179static int
180acpi_memory_get_device(acpi_handle handle,
181 struct acpi_memory_device **mem_device)
182{
183 acpi_status status;
184 acpi_handle phandle;
185 struct acpi_device *device = NULL;
186 struct acpi_device *pdevice = NULL;
187 int result;
188
189 if (!acpi_bus_get_device(handle, &device) && device)
190 goto end;
191
192 status = acpi_get_parent(handle, &phandle);
193 if (ACPI_FAILURE(status)) {
194 pr_warn(PREFIX "Cannot find acpi parent\n");
195 return -EINVAL;
196 }
197
198 /* Get the parent device */
199 result = acpi_bus_get_device(phandle, &pdevice);
200 if (result) {
201 pr_warn(PREFIX "Cannot get acpi bus device\n");
202 return -EINVAL;
203 }
204
205 /*
206 * Now add the notified device. This creates the acpi_device
207 * and invokes .add function
208 */
209 result = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE);
210 if (result) {
211 pr_warn(PREFIX "Cannot add acpi bus\n");
212 return -EINVAL;
213 }
214
215end:
216 *mem_device = acpi_driver_data(device);
217 if (!(*mem_device)) {
218 pr_err(PREFIX "Driver data not found\n");
219 return -ENODEV;
220 }
221
222 return 0;
223}
224
225static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
226{
227 unsigned long long current_status;
228
229 /* Get device present/absent information from the _STA */
230 if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle,
231 "_STA", NULL, &current_status)))
232 return -ENODEV;
233 /*
234 * Check for device status. Device should be
235 * present/enabled/functioning.
236 */
237 if (!((current_status & ACPI_STA_DEVICE_PRESENT)
238 && (current_status & ACPI_STA_DEVICE_ENABLED)
239 && (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
240 return -ENODEV;
241
242 return 0;
243}
244
245static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
246{
247 pr_debug(PREFIX "Xen does not support memory hotremove\n");
248
249 return -ENOSYS;
250}
251
252static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
253{
254 struct acpi_memory_device *mem_device;
255 struct acpi_device *device;
256 u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
257
258 switch (event) {
259 case ACPI_NOTIFY_BUS_CHECK:
260 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
261 "\nReceived BUS CHECK notification for device\n"));
262 /* Fall Through */
263 case ACPI_NOTIFY_DEVICE_CHECK:
264 if (event == ACPI_NOTIFY_DEVICE_CHECK)
265 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
266 "\nReceived DEVICE CHECK notification for device\n"));
267
268 if (acpi_memory_get_device(handle, &mem_device)) {
269 pr_err(PREFIX "Cannot find driver data\n");
270 break;
271 }
272
273 ost_code = ACPI_OST_SC_SUCCESS;
274 break;
275
276 case ACPI_NOTIFY_EJECT_REQUEST:
277 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
278 "\nReceived EJECT REQUEST notification for device\n"));
279
280 if (acpi_bus_get_device(handle, &device)) {
281 pr_err(PREFIX "Device doesn't exist\n");
282 break;
283 }
284 mem_device = acpi_driver_data(device);
285 if (!mem_device) {
286 pr_err(PREFIX "Driver Data is NULL\n");
287 break;
288 }
289
290 /*
291 * TBD: implement acpi_memory_disable_device and invoke
292 * acpi_bus_remove if Xen support hotremove in the future
293 */
294 acpi_memory_disable_device(mem_device);
295 break;
296
297 default:
298 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
299 "Unsupported event [0x%x]\n", event));
300 /* non-hotplug event; possibly handled by other handler */
301 return;
302 }
303
304 (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL);
305 return;
306}
307
308static int xen_acpi_memory_device_add(struct acpi_device *device)
309{
310 int result;
311 struct acpi_memory_device *mem_device = NULL;
312
313
314 if (!device)
315 return -EINVAL;
316
317 mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);
318 if (!mem_device)
319 return -ENOMEM;
320
321 INIT_LIST_HEAD(&mem_device->res_list);
322 mem_device->device = device;
323 sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
324 sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
325 device->driver_data = mem_device;
326
327 /* Get the range from the _CRS */
328 result = acpi_memory_get_device_resources(mem_device);
329 if (result) {
330 kfree(mem_device);
331 return result;
332 }
333
334 /*
335 * For booting existed memory devices, early boot code has recognized
336 * memory area by EFI/E820. If DSDT shows these memory devices on boot,
337 * hotplug is not necessary for them.
338 * For hot-added memory devices during runtime, it need hypercall to
339 * Xen hypervisor to add memory.
340 */
341 if (!acpi_hotmem_initialized)
342 return 0;
343
344 if (!acpi_memory_check_device(mem_device))
345 result = xen_acpi_memory_enable_device(mem_device);
346
347 return result;
348}
349
350static int xen_acpi_memory_device_remove(struct acpi_device *device, int type)
351{
352 struct acpi_memory_device *mem_device = NULL;
353
354 if (!device || !acpi_driver_data(device))
355 return -EINVAL;
356
357 mem_device = acpi_driver_data(device);
358 kfree(mem_device);
359
360 return 0;
361}
362
363/*
364 * Helper function to check for memory device
365 */
366static acpi_status is_memory_device(acpi_handle handle)
367{
368 char *hardware_id;
369 acpi_status status;
370 struct acpi_device_info *info;
371
372 status = acpi_get_object_info(handle, &info);
373 if (ACPI_FAILURE(status))
374 return status;
375
376 if (!(info->valid & ACPI_VALID_HID)) {
377 kfree(info);
378 return AE_ERROR;
379 }
380
381 hardware_id = info->hardware_id.string;
382 if ((hardware_id == NULL) ||
383 (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
384 status = AE_ERROR;
385
386 kfree(info);
387 return status;
388}
389
390static acpi_status
391acpi_memory_register_notify_handler(acpi_handle handle,
392 u32 level, void *ctxt, void **retv)
393{
394 acpi_status status;
395
396 status = is_memory_device(handle);
397 if (ACPI_FAILURE(status))
398 return AE_OK; /* continue */
399
400 status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
401 acpi_memory_device_notify, NULL);
402 /* continue */
403 return AE_OK;
404}
405
406static acpi_status
407acpi_memory_deregister_notify_handler(acpi_handle handle,
408 u32 level, void *ctxt, void **retv)
409{
410 acpi_status status;
411
412 status = is_memory_device(handle);
413 if (ACPI_FAILURE(status))
414 return AE_OK; /* continue */
415
416 status = acpi_remove_notify_handler(handle,
417 ACPI_SYSTEM_NOTIFY,
418 acpi_memory_device_notify);
419
420 return AE_OK; /* continue */
421}
422
423static const struct acpi_device_id memory_device_ids[] = {
424 {ACPI_MEMORY_DEVICE_HID, 0},
425 {"", 0},
426};
427MODULE_DEVICE_TABLE(acpi, memory_device_ids);
428
429static struct acpi_driver xen_acpi_memory_device_driver = {
430 .name = "acpi_memhotplug",
431 .class = ACPI_MEMORY_DEVICE_CLASS,
432 .ids = memory_device_ids,
433 .ops = {
434 .add = xen_acpi_memory_device_add,
435 .remove = xen_acpi_memory_device_remove,
436 },
437};
438
439static int __init xen_acpi_memory_device_init(void)
440{
441 int result;
442 acpi_status status;
443
444 if (!xen_initial_domain())
445 return -ENODEV;
446
447 /* unregister the stub which only used to reserve driver space */
448 xen_stub_memory_device_exit();
449
450 result = acpi_bus_register_driver(&xen_acpi_memory_device_driver);
451 if (result < 0) {
452 xen_stub_memory_device_init();
453 return -ENODEV;
454 }
455
456 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
457 ACPI_UINT32_MAX,
458 acpi_memory_register_notify_handler,
459 NULL, NULL, NULL);
460
461 if (ACPI_FAILURE(status)) {
462 pr_warn(PREFIX "walk_namespace failed\n");
463 acpi_bus_unregister_driver(&xen_acpi_memory_device_driver);
464 xen_stub_memory_device_init();
465 return -ENODEV;
466 }
467
468 acpi_hotmem_initialized = true;
469 return 0;
470}
471
472static void __exit xen_acpi_memory_device_exit(void)
473{
474 acpi_status status;
475
476 if (!xen_initial_domain())
477 return;
478
479 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
480 ACPI_UINT32_MAX,
481 acpi_memory_deregister_notify_handler,
482 NULL, NULL, NULL);
483 if (ACPI_FAILURE(status))
484 pr_warn(PREFIX "walk_namespace failed\n");
485
486 acpi_bus_unregister_driver(&xen_acpi_memory_device_driver);
487
488 /*
489 * stub reserve space again to prevent any chance of native
490 * driver loading.
491 */
492 xen_stub_memory_device_init();
493 return;
494}
495
496module_init(xen_acpi_memory_device_init);
497module_exit(xen_acpi_memory_device_exit);
498ACPI_MODULE_NAME("xen-acpi-memhotplug");
499MODULE_AUTHOR("Liu Jinsong <jinsong.liu@intel.com>");
500MODULE_DESCRIPTION("Xen Hotplug Mem Driver");
501MODULE_LICENSE("GPL");