diff options
Diffstat (limited to 'drivers/acpi/acpi_memhotplug.c')
-rw-r--r-- | drivers/acpi/acpi_memhotplug.c | 193 |
1 files changed, 86 insertions, 107 deletions
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 24c807f96636..eb30e5ab4cab 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/types.h> | 31 | #include <linux/types.h> |
32 | #include <linux/memory_hotplug.h> | 32 | #include <linux/memory_hotplug.h> |
33 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
34 | #include <linux/acpi.h> | ||
34 | #include <acpi/acpi_drivers.h> | 35 | #include <acpi/acpi_drivers.h> |
35 | 36 | ||
36 | #define ACPI_MEMORY_DEVICE_CLASS "memory" | 37 | #define ACPI_MEMORY_DEVICE_CLASS "memory" |
@@ -78,6 +79,7 @@ struct acpi_memory_info { | |||
78 | unsigned short caching; /* memory cache attribute */ | 79 | unsigned short caching; /* memory cache attribute */ |
79 | unsigned short write_protect; /* memory read/write attribute */ | 80 | unsigned short write_protect; /* memory read/write attribute */ |
80 | unsigned int enabled:1; | 81 | unsigned int enabled:1; |
82 | unsigned int failed:1; | ||
81 | }; | 83 | }; |
82 | 84 | ||
83 | struct acpi_memory_device { | 85 | struct acpi_memory_device { |
@@ -86,8 +88,6 @@ struct acpi_memory_device { | |||
86 | struct list_head res_list; | 88 | struct list_head res_list; |
87 | }; | 89 | }; |
88 | 90 | ||
89 | static int acpi_hotmem_initialized; | ||
90 | |||
91 | static acpi_status | 91 | static acpi_status |
92 | acpi_memory_get_resource(struct acpi_resource *resource, void *context) | 92 | acpi_memory_get_resource(struct acpi_resource *resource, void *context) |
93 | { | 93 | { |
@@ -125,12 +125,20 @@ acpi_memory_get_resource(struct acpi_resource *resource, void *context) | |||
125 | return AE_OK; | 125 | return AE_OK; |
126 | } | 126 | } |
127 | 127 | ||
128 | static void | ||
129 | acpi_memory_free_device_resources(struct acpi_memory_device *mem_device) | ||
130 | { | ||
131 | struct acpi_memory_info *info, *n; | ||
132 | |||
133 | list_for_each_entry_safe(info, n, &mem_device->res_list, list) | ||
134 | kfree(info); | ||
135 | INIT_LIST_HEAD(&mem_device->res_list); | ||
136 | } | ||
137 | |||
128 | static int | 138 | static int |
129 | acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) | 139 | acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) |
130 | { | 140 | { |
131 | acpi_status status; | 141 | acpi_status status; |
132 | struct acpi_memory_info *info, *n; | ||
133 | |||
134 | 142 | ||
135 | if (!list_empty(&mem_device->res_list)) | 143 | if (!list_empty(&mem_device->res_list)) |
136 | return 0; | 144 | return 0; |
@@ -138,9 +146,7 @@ acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) | |||
138 | status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS, | 146 | status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS, |
139 | acpi_memory_get_resource, mem_device); | 147 | acpi_memory_get_resource, mem_device); |
140 | if (ACPI_FAILURE(status)) { | 148 | if (ACPI_FAILURE(status)) { |
141 | list_for_each_entry_safe(info, n, &mem_device->res_list, list) | 149 | acpi_memory_free_device_resources(mem_device); |
142 | kfree(info); | ||
143 | INIT_LIST_HEAD(&mem_device->res_list); | ||
144 | return -EINVAL; | 150 | return -EINVAL; |
145 | } | 151 | } |
146 | 152 | ||
@@ -170,7 +176,7 @@ acpi_memory_get_device(acpi_handle handle, | |||
170 | /* Get the parent device */ | 176 | /* Get the parent device */ |
171 | result = acpi_bus_get_device(phandle, &pdevice); | 177 | result = acpi_bus_get_device(phandle, &pdevice); |
172 | if (result) { | 178 | if (result) { |
173 | printk(KERN_WARNING PREFIX "Cannot get acpi bus device"); | 179 | acpi_handle_warn(phandle, "Cannot get acpi bus device\n"); |
174 | return -EINVAL; | 180 | return -EINVAL; |
175 | } | 181 | } |
176 | 182 | ||
@@ -180,14 +186,14 @@ acpi_memory_get_device(acpi_handle handle, | |||
180 | */ | 186 | */ |
181 | result = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE); | 187 | result = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE); |
182 | if (result) { | 188 | if (result) { |
183 | printk(KERN_WARNING PREFIX "Cannot add acpi bus"); | 189 | acpi_handle_warn(handle, "Cannot add acpi bus\n"); |
184 | return -EINVAL; | 190 | return -EINVAL; |
185 | } | 191 | } |
186 | 192 | ||
187 | end: | 193 | end: |
188 | *mem_device = acpi_driver_data(device); | 194 | *mem_device = acpi_driver_data(device); |
189 | if (!(*mem_device)) { | 195 | if (!(*mem_device)) { |
190 | printk(KERN_ERR "\n driver data not found"); | 196 | dev_err(&device->dev, "driver data not found\n"); |
191 | return -ENODEV; | 197 | return -ENODEV; |
192 | } | 198 | } |
193 | 199 | ||
@@ -224,7 +230,8 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) | |||
224 | /* Get the range from the _CRS */ | 230 | /* Get the range from the _CRS */ |
225 | result = acpi_memory_get_device_resources(mem_device); | 231 | result = acpi_memory_get_device_resources(mem_device); |
226 | if (result) { | 232 | if (result) { |
227 | printk(KERN_ERR PREFIX "get_device_resources failed\n"); | 233 | dev_err(&mem_device->device->dev, |
234 | "get_device_resources failed\n"); | ||
228 | mem_device->state = MEMORY_INVALID_STATE; | 235 | mem_device->state = MEMORY_INVALID_STATE; |
229 | return result; | 236 | return result; |
230 | } | 237 | } |
@@ -251,13 +258,27 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) | |||
251 | node = memory_add_physaddr_to_nid(info->start_addr); | 258 | node = memory_add_physaddr_to_nid(info->start_addr); |
252 | 259 | ||
253 | result = add_memory(node, info->start_addr, info->length); | 260 | result = add_memory(node, info->start_addr, info->length); |
254 | if (result) | 261 | |
262 | /* | ||
263 | * If the memory block has been used by the kernel, add_memory() | ||
264 | * returns -EEXIST. If add_memory() returns the other error, it | ||
265 | * means that this memory block is not used by the kernel. | ||
266 | */ | ||
267 | if (result && result != -EEXIST) { | ||
268 | info->failed = 1; | ||
255 | continue; | 269 | continue; |
256 | info->enabled = 1; | 270 | } |
271 | |||
272 | if (!result) | ||
273 | info->enabled = 1; | ||
274 | /* | ||
275 | * Add num_enable even if add_memory() returns -EEXIST, so the | ||
276 | * device is bound to this driver. | ||
277 | */ | ||
257 | num_enabled++; | 278 | num_enabled++; |
258 | } | 279 | } |
259 | if (!num_enabled) { | 280 | if (!num_enabled) { |
260 | printk(KERN_ERR PREFIX "add_memory failed\n"); | 281 | dev_err(&mem_device->device->dev, "add_memory failed\n"); |
261 | mem_device->state = MEMORY_INVALID_STATE; | 282 | mem_device->state = MEMORY_INVALID_STATE; |
262 | return -EINVAL; | 283 | return -EINVAL; |
263 | } | 284 | } |
@@ -272,68 +293,31 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) | |||
272 | return 0; | 293 | return 0; |
273 | } | 294 | } |
274 | 295 | ||
275 | static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device) | 296 | static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) |
276 | { | 297 | { |
277 | acpi_status status; | 298 | int result = 0; |
278 | struct acpi_object_list arg_list; | 299 | struct acpi_memory_info *info, *n; |
279 | union acpi_object arg; | ||
280 | unsigned long long current_status; | ||
281 | |||
282 | |||
283 | /* Issue the _EJ0 command */ | ||
284 | arg_list.count = 1; | ||
285 | arg_list.pointer = &arg; | ||
286 | arg.type = ACPI_TYPE_INTEGER; | ||
287 | arg.integer.value = 1; | ||
288 | status = acpi_evaluate_object(mem_device->device->handle, | ||
289 | "_EJ0", &arg_list, NULL); | ||
290 | /* Return on _EJ0 failure */ | ||
291 | if (ACPI_FAILURE(status)) { | ||
292 | ACPI_EXCEPTION((AE_INFO, status, "_EJ0 failed")); | ||
293 | return -ENODEV; | ||
294 | } | ||
295 | |||
296 | /* Evalute _STA to check if the device is disabled */ | ||
297 | status = acpi_evaluate_integer(mem_device->device->handle, "_STA", | ||
298 | NULL, ¤t_status); | ||
299 | if (ACPI_FAILURE(status)) | ||
300 | return -ENODEV; | ||
301 | |||
302 | /* Check for device status. Device should be disabled */ | ||
303 | if (current_status & ACPI_STA_DEVICE_ENABLED) | ||
304 | return -EINVAL; | ||
305 | 300 | ||
306 | return 0; | 301 | list_for_each_entry_safe(info, n, &mem_device->res_list, list) { |
307 | } | 302 | if (info->failed) |
303 | /* The kernel does not use this memory block */ | ||
304 | continue; | ||
308 | 305 | ||
309 | static int acpi_memory_disable_device(struct acpi_memory_device *mem_device) | 306 | if (!info->enabled) |
310 | { | 307 | /* |
311 | int result; | 308 | * The kernel uses this memory block, but it may be not |
312 | struct acpi_memory_info *info, *n; | 309 | * managed by us. |
310 | */ | ||
311 | return -EBUSY; | ||
313 | 312 | ||
313 | result = remove_memory(info->start_addr, info->length); | ||
314 | if (result) | ||
315 | return result; | ||
314 | 316 | ||
315 | /* | 317 | list_del(&info->list); |
316 | * Ask the VM to offline this memory range. | ||
317 | * Note: Assume that this function returns zero on success | ||
318 | */ | ||
319 | list_for_each_entry_safe(info, n, &mem_device->res_list, list) { | ||
320 | if (info->enabled) { | ||
321 | result = remove_memory(info->start_addr, info->length); | ||
322 | if (result) | ||
323 | return result; | ||
324 | } | ||
325 | kfree(info); | 318 | kfree(info); |
326 | } | 319 | } |
327 | 320 | ||
328 | /* Power-off and eject the device */ | ||
329 | result = acpi_memory_powerdown_device(mem_device); | ||
330 | if (result) { | ||
331 | /* Set the status of the device to invalid */ | ||
332 | mem_device->state = MEMORY_INVALID_STATE; | ||
333 | return result; | ||
334 | } | ||
335 | |||
336 | mem_device->state = MEMORY_POWER_OFF_STATE; | ||
337 | return result; | 321 | return result; |
338 | } | 322 | } |
339 | 323 | ||
@@ -341,6 +325,7 @@ static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) | |||
341 | { | 325 | { |
342 | struct acpi_memory_device *mem_device; | 326 | struct acpi_memory_device *mem_device; |
343 | struct acpi_device *device; | 327 | struct acpi_device *device; |
328 | struct acpi_eject_event *ej_event = NULL; | ||
344 | u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ | 329 | u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ |
345 | 330 | ||
346 | switch (event) { | 331 | switch (event) { |
@@ -353,7 +338,7 @@ static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) | |||
353 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 338 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
354 | "\nReceived DEVICE CHECK notification for device\n")); | 339 | "\nReceived DEVICE CHECK notification for device\n")); |
355 | if (acpi_memory_get_device(handle, &mem_device)) { | 340 | if (acpi_memory_get_device(handle, &mem_device)) { |
356 | printk(KERN_ERR PREFIX "Cannot find driver data\n"); | 341 | acpi_handle_err(handle, "Cannot find driver data\n"); |
357 | break; | 342 | break; |
358 | } | 343 | } |
359 | 344 | ||
@@ -361,7 +346,7 @@ static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) | |||
361 | break; | 346 | break; |
362 | 347 | ||
363 | if (acpi_memory_enable_device(mem_device)) { | 348 | if (acpi_memory_enable_device(mem_device)) { |
364 | printk(KERN_ERR PREFIX "Cannot enable memory device\n"); | 349 | acpi_handle_err(handle,"Cannot enable memory device\n"); |
365 | break; | 350 | break; |
366 | } | 351 | } |
367 | 352 | ||
@@ -373,40 +358,28 @@ static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) | |||
373 | "\nReceived EJECT REQUEST notification for device\n")); | 358 | "\nReceived EJECT REQUEST notification for device\n")); |
374 | 359 | ||
375 | if (acpi_bus_get_device(handle, &device)) { | 360 | if (acpi_bus_get_device(handle, &device)) { |
376 | printk(KERN_ERR PREFIX "Device doesn't exist\n"); | 361 | acpi_handle_err(handle, "Device doesn't exist\n"); |
377 | break; | 362 | break; |
378 | } | 363 | } |
379 | mem_device = acpi_driver_data(device); | 364 | mem_device = acpi_driver_data(device); |
380 | if (!mem_device) { | 365 | if (!mem_device) { |
381 | printk(KERN_ERR PREFIX "Driver Data is NULL\n"); | 366 | acpi_handle_err(handle, "Driver Data is NULL\n"); |
382 | break; | 367 | break; |
383 | } | 368 | } |
384 | 369 | ||
385 | /* | 370 | ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); |
386 | * Currently disabling memory device from kernel mode | 371 | if (!ej_event) { |
387 | * TBD: Can also be disabled from user mode scripts | 372 | pr_err(PREFIX "No memory, dropping EJECT\n"); |
388 | * TBD: Can also be disabled by Callback registration | ||
389 | * with generic sysfs driver | ||
390 | */ | ||
391 | if (acpi_memory_disable_device(mem_device)) { | ||
392 | printk(KERN_ERR PREFIX "Disable memory device\n"); | ||
393 | /* | ||
394 | * If _EJ0 was called but failed, _OST is not | ||
395 | * necessary. | ||
396 | */ | ||
397 | if (mem_device->state == MEMORY_INVALID_STATE) | ||
398 | return; | ||
399 | |||
400 | break; | 373 | break; |
401 | } | 374 | } |
402 | 375 | ||
403 | /* | 376 | ej_event->handle = handle; |
404 | * TBD: Invoke acpi_bus_remove to cleanup data structures | 377 | ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; |
405 | */ | 378 | acpi_os_hotplug_execute(acpi_bus_hot_remove_device, |
379 | (void *)ej_event); | ||
406 | 380 | ||
407 | /* _EJ0 succeeded; _OST is not necessary */ | 381 | /* eject is performed asynchronously */ |
408 | return; | 382 | return; |
409 | |||
410 | default: | 383 | default: |
411 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 384 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
412 | "Unsupported event [0x%x]\n", event)); | 385 | "Unsupported event [0x%x]\n", event)); |
@@ -420,6 +393,15 @@ static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) | |||
420 | return; | 393 | return; |
421 | } | 394 | } |
422 | 395 | ||
396 | static void acpi_memory_device_free(struct acpi_memory_device *mem_device) | ||
397 | { | ||
398 | if (!mem_device) | ||
399 | return; | ||
400 | |||
401 | acpi_memory_free_device_resources(mem_device); | ||
402 | kfree(mem_device); | ||
403 | } | ||
404 | |||
423 | static int acpi_memory_device_add(struct acpi_device *device) | 405 | static int acpi_memory_device_add(struct acpi_device *device) |
424 | { | 406 | { |
425 | int result; | 407 | int result; |
@@ -449,23 +431,16 @@ static int acpi_memory_device_add(struct acpi_device *device) | |||
449 | /* Set the device state */ | 431 | /* Set the device state */ |
450 | mem_device->state = MEMORY_POWER_ON_STATE; | 432 | mem_device->state = MEMORY_POWER_ON_STATE; |
451 | 433 | ||
452 | printk(KERN_DEBUG "%s \n", acpi_device_name(device)); | 434 | pr_debug("%s\n", acpi_device_name(device)); |
453 | |||
454 | /* | ||
455 | * Early boot code has recognized memory area by EFI/E820. | ||
456 | * If DSDT shows these memory devices on boot, hotplug is not necessary | ||
457 | * for them. So, it just returns until completion of this driver's | ||
458 | * start up. | ||
459 | */ | ||
460 | if (!acpi_hotmem_initialized) | ||
461 | return 0; | ||
462 | 435 | ||
463 | if (!acpi_memory_check_device(mem_device)) { | 436 | if (!acpi_memory_check_device(mem_device)) { |
464 | /* call add_memory func */ | 437 | /* call add_memory func */ |
465 | result = acpi_memory_enable_device(mem_device); | 438 | result = acpi_memory_enable_device(mem_device); |
466 | if (result) | 439 | if (result) { |
467 | printk(KERN_ERR PREFIX | 440 | dev_err(&device->dev, |
468 | "Error in acpi_memory_enable_device\n"); | 441 | "Error in acpi_memory_enable_device\n"); |
442 | acpi_memory_device_free(mem_device); | ||
443 | } | ||
469 | } | 444 | } |
470 | return result; | 445 | return result; |
471 | } | 446 | } |
@@ -473,13 +448,18 @@ static int acpi_memory_device_add(struct acpi_device *device) | |||
473 | static int acpi_memory_device_remove(struct acpi_device *device, int type) | 448 | static int acpi_memory_device_remove(struct acpi_device *device, int type) |
474 | { | 449 | { |
475 | struct acpi_memory_device *mem_device = NULL; | 450 | struct acpi_memory_device *mem_device = NULL; |
476 | 451 | int result; | |
477 | 452 | ||
478 | if (!device || !acpi_driver_data(device)) | 453 | if (!device || !acpi_driver_data(device)) |
479 | return -EINVAL; | 454 | return -EINVAL; |
480 | 455 | ||
481 | mem_device = acpi_driver_data(device); | 456 | mem_device = acpi_driver_data(device); |
482 | kfree(mem_device); | 457 | |
458 | result = acpi_memory_remove_memory(mem_device); | ||
459 | if (result) | ||
460 | return result; | ||
461 | |||
462 | acpi_memory_device_free(mem_device); | ||
483 | 463 | ||
484 | return 0; | 464 | return 0; |
485 | } | 465 | } |
@@ -568,7 +548,6 @@ static int __init acpi_memory_device_init(void) | |||
568 | return -ENODEV; | 548 | return -ENODEV; |
569 | } | 549 | } |
570 | 550 | ||
571 | acpi_hotmem_initialized = 1; | ||
572 | return 0; | 551 | return 0; |
573 | } | 552 | } |
574 | 553 | ||