aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/xen-acpi-memhotplug.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-memhotplug.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-memhotplug.c')
-rw-r--r--drivers/xen/xen-acpi-memhotplug.c483
1 files changed, 483 insertions, 0 deletions
diff --git a/drivers/xen/xen-acpi-memhotplug.c b/drivers/xen/xen-acpi-memhotplug.c
new file mode 100644
index 000000000000..853b12dba5bb
--- /dev/null
+++ b/drivers/xen/xen-acpi-memhotplug.c
@@ -0,0 +1,483 @@
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_memory_enable_device(struct acpi_memory_device *mem_device)
69{
70 int pxm, result;
71 int num_enabled = 0;
72 struct acpi_memory_info *info;
73
74 if (!mem_device)
75 return -EINVAL;
76
77 pxm = xen_acpi_get_pxm(mem_device->device->handle);
78 if (pxm < 0)
79 return pxm;
80
81 list_for_each_entry(info, &mem_device->res_list, list) {
82 if (info->enabled) { /* just sanity check...*/
83 num_enabled++;
84 continue;
85 }
86
87 if (!info->length)
88 continue;
89
90 result = xen_hotadd_memory(pxm, info);
91 if (result)
92 continue;
93 info->enabled = 1;
94 num_enabled++;
95 }
96
97 if (!num_enabled)
98 return -ENODEV;
99
100 return 0;
101}
102
103static acpi_status
104acpi_memory_get_resource(struct acpi_resource *resource, void *context)
105{
106 struct acpi_memory_device *mem_device = context;
107 struct acpi_resource_address64 address64;
108 struct acpi_memory_info *info, *new;
109 acpi_status status;
110
111 status = acpi_resource_to_address64(resource, &address64);
112 if (ACPI_FAILURE(status) ||
113 (address64.resource_type != ACPI_MEMORY_RANGE))
114 return AE_OK;
115
116 list_for_each_entry(info, &mem_device->res_list, list) {
117 if ((info->caching == address64.info.mem.caching) &&
118 (info->write_protect == address64.info.mem.write_protect) &&
119 (info->start_addr + info->length == address64.minimum)) {
120 info->length += address64.address_length;
121 return AE_OK;
122 }
123 }
124
125 new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL);
126 if (!new)
127 return AE_ERROR;
128
129 INIT_LIST_HEAD(&new->list);
130 new->caching = address64.info.mem.caching;
131 new->write_protect = address64.info.mem.write_protect;
132 new->start_addr = address64.minimum;
133 new->length = address64.address_length;
134 list_add_tail(&new->list, &mem_device->res_list);
135
136 return AE_OK;
137}
138
139static int
140acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
141{
142 acpi_status status;
143 struct acpi_memory_info *info, *n;
144
145 if (!list_empty(&mem_device->res_list))
146 return 0;
147
148 status = acpi_walk_resources(mem_device->device->handle,
149 METHOD_NAME__CRS, acpi_memory_get_resource, mem_device);
150
151 if (ACPI_FAILURE(status)) {
152 list_for_each_entry_safe(info, n, &mem_device->res_list, list)
153 kfree(info);
154 INIT_LIST_HEAD(&mem_device->res_list);
155 return -EINVAL;
156 }
157
158 return 0;
159}
160
161static int
162acpi_memory_get_device(acpi_handle handle,
163 struct acpi_memory_device **mem_device)
164{
165 acpi_status status;
166 acpi_handle phandle;
167 struct acpi_device *device = NULL;
168 struct acpi_device *pdevice = NULL;
169 int result;
170
171 if (!acpi_bus_get_device(handle, &device) && device)
172 goto end;
173
174 status = acpi_get_parent(handle, &phandle);
175 if (ACPI_FAILURE(status)) {
176 pr_warn(PREFIX "Cannot find acpi parent\n");
177 return -EINVAL;
178 }
179
180 /* Get the parent device */
181 result = acpi_bus_get_device(phandle, &pdevice);
182 if (result) {
183 pr_warn(PREFIX "Cannot get acpi bus device\n");
184 return -EINVAL;
185 }
186
187 /*
188 * Now add the notified device. This creates the acpi_device
189 * and invokes .add function
190 */
191 result = acpi_bus_scan(handle);
192 if (result) {
193 pr_warn(PREFIX "Cannot add acpi bus\n");
194 return -EINVAL;
195 }
196
197end:
198 *mem_device = acpi_driver_data(device);
199 if (!(*mem_device)) {
200 pr_err(PREFIX "Driver data not found\n");
201 return -ENODEV;
202 }
203
204 return 0;
205}
206
207static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
208{
209 unsigned long long current_status;
210
211 /* Get device present/absent information from the _STA */
212 if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle,
213 "_STA", NULL, &current_status)))
214 return -ENODEV;
215 /*
216 * Check for device status. Device should be
217 * present/enabled/functioning.
218 */
219 if (!((current_status & ACPI_STA_DEVICE_PRESENT)
220 && (current_status & ACPI_STA_DEVICE_ENABLED)
221 && (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
222 return -ENODEV;
223
224 return 0;
225}
226
227static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
228{
229 pr_debug(PREFIX "Xen does not support memory hotremove\n");
230
231 return -ENOSYS;
232}
233
234static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
235{
236 struct acpi_memory_device *mem_device;
237 struct acpi_device *device;
238 u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
239
240 switch (event) {
241 case ACPI_NOTIFY_BUS_CHECK:
242 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
243 "\nReceived BUS CHECK notification for device\n"));
244 /* Fall Through */
245 case ACPI_NOTIFY_DEVICE_CHECK:
246 if (event == ACPI_NOTIFY_DEVICE_CHECK)
247 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
248 "\nReceived DEVICE CHECK notification for device\n"));
249
250 if (acpi_memory_get_device(handle, &mem_device)) {
251 pr_err(PREFIX "Cannot find driver data\n");
252 break;
253 }
254
255 ost_code = ACPI_OST_SC_SUCCESS;
256 break;
257
258 case ACPI_NOTIFY_EJECT_REQUEST:
259 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
260 "\nReceived EJECT REQUEST notification for device\n"));
261
262 if (acpi_bus_get_device(handle, &device)) {
263 pr_err(PREFIX "Device doesn't exist\n");
264 break;
265 }
266 mem_device = acpi_driver_data(device);
267 if (!mem_device) {
268 pr_err(PREFIX "Driver Data is NULL\n");
269 break;
270 }
271
272 /*
273 * TBD: implement acpi_memory_disable_device and invoke
274 * acpi_bus_remove if Xen support hotremove in the future
275 */
276 acpi_memory_disable_device(mem_device);
277 break;
278
279 default:
280 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
281 "Unsupported event [0x%x]\n", event));
282 /* non-hotplug event; possibly handled by other handler */
283 return;
284 }
285
286 (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL);
287 return;
288}
289
290static int xen_acpi_memory_device_add(struct acpi_device *device)
291{
292 int result;
293 struct acpi_memory_device *mem_device = NULL;
294
295
296 if (!device)
297 return -EINVAL;
298
299 mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);
300 if (!mem_device)
301 return -ENOMEM;
302
303 INIT_LIST_HEAD(&mem_device->res_list);
304 mem_device->device = device;
305 sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
306 sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
307 device->driver_data = mem_device;
308
309 /* Get the range from the _CRS */
310 result = acpi_memory_get_device_resources(mem_device);
311 if (result) {
312 kfree(mem_device);
313 return result;
314 }
315
316 /*
317 * For booting existed memory devices, early boot code has recognized
318 * memory area by EFI/E820. If DSDT shows these memory devices on boot,
319 * hotplug is not necessary for them.
320 * For hot-added memory devices during runtime, it need hypercall to
321 * Xen hypervisor to add memory.
322 */
323 if (!acpi_hotmem_initialized)
324 return 0;
325
326 if (!acpi_memory_check_device(mem_device))
327 result = xen_acpi_memory_enable_device(mem_device);
328
329 return result;
330}
331
332static int xen_acpi_memory_device_remove(struct acpi_device *device)
333{
334 struct acpi_memory_device *mem_device = NULL;
335
336 if (!device || !acpi_driver_data(device))
337 return -EINVAL;
338
339 mem_device = acpi_driver_data(device);
340 kfree(mem_device);
341
342 return 0;
343}
344
345/*
346 * Helper function to check for memory device
347 */
348static acpi_status is_memory_device(acpi_handle handle)
349{
350 char *hardware_id;
351 acpi_status status;
352 struct acpi_device_info *info;
353
354 status = acpi_get_object_info(handle, &info);
355 if (ACPI_FAILURE(status))
356 return status;
357
358 if (!(info->valid & ACPI_VALID_HID)) {
359 kfree(info);
360 return AE_ERROR;
361 }
362
363 hardware_id = info->hardware_id.string;
364 if ((hardware_id == NULL) ||
365 (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
366 status = AE_ERROR;
367
368 kfree(info);
369 return status;
370}
371
372static acpi_status
373acpi_memory_register_notify_handler(acpi_handle handle,
374 u32 level, void *ctxt, void **retv)
375{
376 acpi_status status;
377
378 status = is_memory_device(handle);
379 if (ACPI_FAILURE(status))
380 return AE_OK; /* continue */
381
382 status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
383 acpi_memory_device_notify, NULL);
384 /* continue */
385 return AE_OK;
386}
387
388static acpi_status
389acpi_memory_deregister_notify_handler(acpi_handle handle,
390 u32 level, void *ctxt, void **retv)
391{
392 acpi_status status;
393
394 status = is_memory_device(handle);
395 if (ACPI_FAILURE(status))
396 return AE_OK; /* continue */
397
398 status = acpi_remove_notify_handler(handle,
399 ACPI_SYSTEM_NOTIFY,
400 acpi_memory_device_notify);
401
402 return AE_OK; /* continue */
403}
404
405static const struct acpi_device_id memory_device_ids[] = {
406 {ACPI_MEMORY_DEVICE_HID, 0},
407 {"", 0},
408};
409MODULE_DEVICE_TABLE(acpi, memory_device_ids);
410
411static struct acpi_driver xen_acpi_memory_device_driver = {
412 .name = "acpi_memhotplug",
413 .class = ACPI_MEMORY_DEVICE_CLASS,
414 .ids = memory_device_ids,
415 .ops = {
416 .add = xen_acpi_memory_device_add,
417 .remove = xen_acpi_memory_device_remove,
418 },
419};
420
421static int __init xen_acpi_memory_device_init(void)
422{
423 int result;
424 acpi_status status;
425
426 if (!xen_initial_domain())
427 return -ENODEV;
428
429 /* unregister the stub which only used to reserve driver space */
430 xen_stub_memory_device_exit();
431
432 result = acpi_bus_register_driver(&xen_acpi_memory_device_driver);
433 if (result < 0) {
434 xen_stub_memory_device_init();
435 return -ENODEV;
436 }
437
438 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
439 ACPI_UINT32_MAX,
440 acpi_memory_register_notify_handler,
441 NULL, NULL, NULL);
442
443 if (ACPI_FAILURE(status)) {
444 pr_warn(PREFIX "walk_namespace failed\n");
445 acpi_bus_unregister_driver(&xen_acpi_memory_device_driver);
446 xen_stub_memory_device_init();
447 return -ENODEV;
448 }
449
450 acpi_hotmem_initialized = true;
451 return 0;
452}
453
454static void __exit xen_acpi_memory_device_exit(void)
455{
456 acpi_status status;
457
458 if (!xen_initial_domain())
459 return;
460
461 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
462 ACPI_UINT32_MAX,
463 acpi_memory_deregister_notify_handler,
464 NULL, NULL, NULL);
465 if (ACPI_FAILURE(status))
466 pr_warn(PREFIX "walk_namespace failed\n");
467
468 acpi_bus_unregister_driver(&xen_acpi_memory_device_driver);
469
470 /*
471 * stub reserve space again to prevent any chance of native
472 * driver loading.
473 */
474 xen_stub_memory_device_init();
475 return;
476}
477
478module_init(xen_acpi_memory_device_init);
479module_exit(xen_acpi_memory_device_exit);
480ACPI_MODULE_NAME("xen-acpi-memhotplug");
481MODULE_AUTHOR("Liu Jinsong <jinsong.liu@intel.com>");
482MODULE_DESCRIPTION("Xen Hotplug Mem Driver");
483MODULE_LICENSE("GPL");