aboutsummaryrefslogtreecommitdiff
path: root/drivers/acpi/acpi_memhotplug.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/acpi_memhotplug.c')
-rw-r--r--drivers/acpi/acpi_memhotplug.c486
1 files changed, 152 insertions, 334 deletions
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
index d9857138565..23e2319ead4 100644
--- a/drivers/acpi/acpi_memhotplug.c
+++ b/drivers/acpi/acpi_memhotplug.c
@@ -1,5 +1,7 @@
/*
- * Copyright (C) 2004 Intel Corporation <naveen.b.s@intel.com>
+ * Copyright (C) 2004, 2013 Intel Corporation
+ * Author: Naveen B S <naveen.b.s@intel.com>
+ * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
*
* All rights reserved.
*
@@ -25,13 +27,11 @@
* ranges.
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
+#include <linux/acpi.h>
+#include <linux/memory.h>
#include <linux/memory_hotplug.h>
-#include <linux/slab.h>
-#include <acpi/acpi_drivers.h>
+
+#include "internal.h"
#define ACPI_MEMORY_DEVICE_CLASS "memory"
#define ACPI_MEMORY_DEVICE_HID "PNP0C80"
@@ -43,32 +43,30 @@
#define PREFIX "ACPI:memory_hp:"
ACPI_MODULE_NAME("acpi_memhotplug");
-MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>");
-MODULE_DESCRIPTION("Hotplug Mem Driver");
-MODULE_LICENSE("GPL");
+
+static const struct acpi_device_id memory_device_ids[] = {
+ {ACPI_MEMORY_DEVICE_HID, 0},
+ {"", 0},
+};
+
+#ifdef CONFIG_ACPI_HOTPLUG_MEMORY
/* Memory Device States */
#define MEMORY_INVALID_STATE 0
#define MEMORY_POWER_ON_STATE 1
#define MEMORY_POWER_OFF_STATE 2
-static int acpi_memory_device_add(struct acpi_device *device);
-static int acpi_memory_device_remove(struct acpi_device *device, int type);
+static int acpi_memory_device_add(struct acpi_device *device,
+ const struct acpi_device_id *not_used);
+static void acpi_memory_device_remove(struct acpi_device *device);
-static const struct acpi_device_id memory_device_ids[] = {
- {ACPI_MEMORY_DEVICE_HID, 0},
- {"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, memory_device_ids);
-
-static struct acpi_driver acpi_memory_device_driver = {
- .name = "acpi_memhotplug",
- .class = ACPI_MEMORY_DEVICE_CLASS,
+static struct acpi_scan_handler memory_device_handler = {
.ids = memory_device_ids,
- .ops = {
- .add = acpi_memory_device_add,
- .remove = acpi_memory_device_remove,
- },
+ .attach = acpi_memory_device_add,
+ .detach = acpi_memory_device_remove,
+ .hotplug = {
+ .enabled = true,
+ },
};
struct acpi_memory_info {
@@ -86,8 +84,6 @@ struct acpi_memory_device {
struct list_head res_list;
};
-static int acpi_hotmem_initialized;
-
static acpi_status
acpi_memory_get_resource(struct acpi_resource *resource, void *context)
{
@@ -125,72 +121,31 @@ acpi_memory_get_resource(struct acpi_resource *resource, void *context)
return AE_OK;
}
-static int
-acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
+static void
+acpi_memory_free_device_resources(struct acpi_memory_device *mem_device)
{
- acpi_status status;
struct acpi_memory_info *info, *n;
-
- if (!list_empty(&mem_device->res_list))
- return 0;
-
- status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS,
- acpi_memory_get_resource, mem_device);
- if (ACPI_FAILURE(status)) {
- list_for_each_entry_safe(info, n, &mem_device->res_list, list)
- kfree(info);
- INIT_LIST_HEAD(&mem_device->res_list);
- return -EINVAL;
- }
-
- return 0;
+ list_for_each_entry_safe(info, n, &mem_device->res_list, list)
+ kfree(info);
+ INIT_LIST_HEAD(&mem_device->res_list);
}
static int
-acpi_memory_get_device(acpi_handle handle,
- struct acpi_memory_device **mem_device)
+acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
{
acpi_status status;
- acpi_handle phandle;
- struct acpi_device *device = NULL;
- struct acpi_device *pdevice = NULL;
- int result;
+ if (!list_empty(&mem_device->res_list))
+ return 0;
- if (!acpi_bus_get_device(handle, &device) && device)
- goto end;
-
- status = acpi_get_parent(handle, &phandle);
+ status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS,
+ acpi_memory_get_resource, mem_device);
if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status, "Cannot find acpi parent"));
- return -EINVAL;
- }
-
- /* Get the parent device */
- result = acpi_bus_get_device(phandle, &pdevice);
- if (result) {
- printk(KERN_WARNING PREFIX "Cannot get acpi bus device");
- return -EINVAL;
- }
-
- /*
- * Now add the notified device. This creates the acpi_device
- * and invokes .add function
- */
- result = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE);
- if (result) {
- printk(KERN_WARNING PREFIX "Cannot add acpi bus");
+ acpi_memory_free_device_resources(mem_device);
return -EINVAL;
}
- end:
- *mem_device = acpi_driver_data(device);
- if (!(*mem_device)) {
- printk(KERN_ERR "\n driver data not found");
- return -ENODEV;
- }
-
return 0;
}
@@ -199,8 +154,9 @@ static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
unsigned long long current_status;
/* Get device present/absent information from the _STA */
- if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle, "_STA",
- NULL, &current_status)))
+ if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle,
+ METHOD_NAME__STA, NULL,
+ &current_status)))
return -ENODEV;
/*
* Check for device status. Device should be
@@ -214,22 +170,49 @@ static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
return 0;
}
+static unsigned long acpi_meminfo_start_pfn(struct acpi_memory_info *info)
+{
+ return PFN_DOWN(info->start_addr);
+}
+
+static unsigned long acpi_meminfo_end_pfn(struct acpi_memory_info *info)
+{
+ return PFN_UP(info->start_addr + info->length-1);
+}
+
+static int acpi_bind_memblk(struct memory_block *mem, void *arg)
+{
+ return acpi_bind_one(&mem->dev, arg);
+}
+
+static int acpi_bind_memory_blocks(struct acpi_memory_info *info,
+ struct acpi_device *adev)
+{
+ return walk_memory_range(acpi_meminfo_start_pfn(info),
+ acpi_meminfo_end_pfn(info), adev,
+ acpi_bind_memblk);
+}
+
+static int acpi_unbind_memblk(struct memory_block *mem, void *arg)
+{
+ acpi_unbind_one(&mem->dev);
+ return 0;
+}
+
+static void acpi_unbind_memory_blocks(struct acpi_memory_info *info)
+{
+ walk_memory_range(acpi_meminfo_start_pfn(info),
+ acpi_meminfo_end_pfn(info), NULL, acpi_unbind_memblk);
+}
+
static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
{
+ acpi_handle handle = mem_device->device->handle;
int result, num_enabled = 0;
struct acpi_memory_info *info;
int node;
-
- /* Get the range from the _CRS */
- result = acpi_memory_get_device_resources(mem_device);
- if (result) {
- printk(KERN_ERR PREFIX "get_device_resources failed\n");
- mem_device->state = MEMORY_INVALID_STATE;
- return result;
- }
-
- node = acpi_get_node(mem_device->device->handle);
+ node = acpi_get_node(handle);
/*
* Tell the VM there is more memory here...
* Note: Assume that this function returns zero on success
@@ -251,13 +234,31 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
node = memory_add_physaddr_to_nid(info->start_addr);
result = add_memory(node, info->start_addr, info->length);
- if (result)
+
+ /*
+ * If the memory block has been used by the kernel, add_memory()
+ * returns -EEXIST. If add_memory() returns the other error, it
+ * means that this memory block is not used by the kernel.
+ */
+ if (result && result != -EEXIST)
continue;
+
+ result = acpi_bind_memory_blocks(info, mem_device->device);
+ if (result) {
+ acpi_unbind_memory_blocks(info);
+ return -ENODEV;
+ }
+
info->enabled = 1;
+
+ /*
+ * Add num_enable even if add_memory() returns -EEXIST, so the
+ * device is bound to this driver.
+ */
num_enabled++;
}
if (!num_enabled) {
- printk(KERN_ERR PREFIX "add_memory failed\n");
+ dev_err(&mem_device->device->dev, "add_memory failed\n");
mem_device->state = MEMORY_INVALID_STATE;
return -EINVAL;
}
@@ -272,138 +273,41 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
return 0;
}
-static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device)
+static void acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
{
- acpi_status status;
- struct acpi_object_list arg_list;
- union acpi_object arg;
- unsigned long long current_status;
-
-
- /* Issue the _EJ0 command */
- arg_list.count = 1;
- arg_list.pointer = &arg;
- arg.type = ACPI_TYPE_INTEGER;
- arg.integer.value = 1;
- status = acpi_evaluate_object(mem_device->device->handle,
- "_EJ0", &arg_list, NULL);
- /* Return on _EJ0 failure */
- if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status, "_EJ0 failed"));
- return -ENODEV;
- }
-
- /* Evalute _STA to check if the device is disabled */
- status = acpi_evaluate_integer(mem_device->device->handle, "_STA",
- NULL, &current_status);
- if (ACPI_FAILURE(status))
- return -ENODEV;
-
- /* Check for device status. Device should be disabled */
- if (current_status & ACPI_STA_DEVICE_ENABLED)
- return -EINVAL;
-
- return 0;
-}
-
-static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
-{
- int result;
+ acpi_handle handle = mem_device->device->handle;
struct acpi_memory_info *info, *n;
+ int nid = acpi_get_node(handle);
-
- /*
- * Ask the VM to offline this memory range.
- * Note: Assume that this function returns zero on success
- */
list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
- if (info->enabled) {
- result = remove_memory(info->start_addr, info->length);
- if (result)
- return result;
- }
- kfree(info);
- }
+ if (!info->enabled)
+ continue;
- /* Power-off and eject the device */
- result = acpi_memory_powerdown_device(mem_device);
- if (result) {
- /* Set the status of the device to invalid */
- mem_device->state = MEMORY_INVALID_STATE;
- return result;
- }
+ if (nid == NUMA_NO_NODE)
+ nid = memory_add_physaddr_to_nid(info->start_addr);
- mem_device->state = MEMORY_POWER_OFF_STATE;
- return result;
+ acpi_unbind_memory_blocks(info);
+ remove_memory(nid, info->start_addr, info->length);
+ list_del(&info->list);
+ kfree(info);
+ }
}
-static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
+static void acpi_memory_device_free(struct acpi_memory_device *mem_device)
{
- struct acpi_memory_device *mem_device;
- struct acpi_device *device;
-
-
- switch (event) {
- case ACPI_NOTIFY_BUS_CHECK:
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "\nReceived BUS CHECK notification for device\n"));
- /* Fall Through */
- case ACPI_NOTIFY_DEVICE_CHECK:
- if (event == ACPI_NOTIFY_DEVICE_CHECK)
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "\nReceived DEVICE CHECK notification for device\n"));
- if (acpi_memory_get_device(handle, &mem_device)) {
- printk(KERN_ERR PREFIX "Cannot find driver data\n");
- return;
- }
-
- if (!acpi_memory_check_device(mem_device)) {
- if (acpi_memory_enable_device(mem_device))
- printk(KERN_ERR PREFIX
- "Cannot enable memory device\n");
- }
- break;
- case ACPI_NOTIFY_EJECT_REQUEST:
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "\nReceived EJECT REQUEST notification for device\n"));
-
- if (acpi_bus_get_device(handle, &device)) {
- printk(KERN_ERR PREFIX "Device doesn't exist\n");
- break;
- }
- mem_device = acpi_driver_data(device);
- if (!mem_device) {
- printk(KERN_ERR PREFIX "Driver Data is NULL\n");
- break;
- }
-
- /*
- * Currently disabling memory device from kernel mode
- * TBD: Can also be disabled from user mode scripts
- * TBD: Can also be disabled by Callback registration
- * with generic sysfs driver
- */
- if (acpi_memory_disable_device(mem_device))
- printk(KERN_ERR PREFIX
- "Disable memory device\n");
- /*
- * TBD: Invoke acpi_bus_remove to cleanup data structures
- */
- break;
- default:
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Unsupported event [0x%x]\n", event));
- break;
- }
+ if (!mem_device)
+ return;
- return;
+ acpi_memory_free_device_resources(mem_device);
+ mem_device->device->driver_data = NULL;
+ kfree(mem_device);
}
-static int acpi_memory_device_add(struct acpi_device *device)
+static int acpi_memory_device_add(struct acpi_device *device,
+ const struct acpi_device_id *not_used)
{
+ struct acpi_memory_device *mem_device;
int result;
- struct acpi_memory_device *mem_device = NULL;
-
if (!device)
return -EINVAL;
@@ -421,6 +325,7 @@ static int acpi_memory_device_add(struct acpi_device *device)
/* Get the range from the _CRS */
result = acpi_memory_get_device_resources(mem_device);
if (result) {
+ device->driver_data = NULL;
kfree(mem_device);
return result;
}
@@ -428,150 +333,63 @@ static int acpi_memory_device_add(struct acpi_device *device)
/* Set the device state */
mem_device->state = MEMORY_POWER_ON_STATE;
- printk(KERN_DEBUG "%s \n", acpi_device_name(device));
-
- /*
- * Early boot code has recognized memory area by EFI/E820.
- * If DSDT shows these memory devices on boot, hotplug is not necessary
- * for them. So, it just returns until completion of this driver's
- * start up.
- */
- if (!acpi_hotmem_initialized)
+ result = acpi_memory_check_device(mem_device);
+ if (result) {
+ acpi_memory_device_free(mem_device);
return 0;
+ }
- if (!acpi_memory_check_device(mem_device)) {
- /* call add_memory func */
- result = acpi_memory_enable_device(mem_device);
- if (result)
- printk(KERN_ERR PREFIX
- "Error in acpi_memory_enable_device\n");
+ result = acpi_memory_enable_device(mem_device);
+ if (result) {
+ dev_err(&device->dev, "acpi_memory_enable_device() error\n");
+ acpi_memory_device_free(mem_device);
+ return result;
}
- return result;
+
+ dev_dbg(&device->dev, "Memory device configured by ACPI\n");
+ return 1;
}
-static int acpi_memory_device_remove(struct acpi_device *device, int type)
+static void acpi_memory_device_remove(struct acpi_device *device)
{
- struct acpi_memory_device *mem_device = NULL;
-
+ struct acpi_memory_device *mem_device;
if (!device || !acpi_driver_data(device))
- return -EINVAL;
+ return;
mem_device = acpi_driver_data(device);
- kfree(mem_device);
-
- return 0;
+ acpi_memory_remove_memory(mem_device);
+ acpi_memory_device_free(mem_device);
}
-/*
- * Helper function to check for memory device
- */
-static acpi_status is_memory_device(acpi_handle handle)
-{
- char *hardware_id;
- acpi_status status;
- struct acpi_device_info *info;
-
- status = acpi_get_object_info(handle, &info);
- if (ACPI_FAILURE(status))
- return status;
-
- if (!(info->valid & ACPI_VALID_HID)) {
- kfree(info);
- return AE_ERROR;
- }
-
- hardware_id = info->hardware_id.string;
- if ((hardware_id == NULL) ||
- (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
- status = AE_ERROR;
+static bool __initdata acpi_no_memhotplug;
- kfree(info);
- return status;
-}
-
-static acpi_status
-acpi_memory_register_notify_handler(acpi_handle handle,
- u32 level, void *ctxt, void **retv)
+void __init acpi_memory_hotplug_init(void)
{
- acpi_status status;
-
-
- status = is_memory_device(handle);
- if (ACPI_FAILURE(status))
- return AE_OK; /* continue */
-
- status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
- acpi_memory_device_notify, NULL);
- /* continue */
- return AE_OK;
+ if (acpi_no_memhotplug) {
+ memory_device_handler.attach = NULL;
+ acpi_scan_add_handler(&memory_device_handler);
+ return;
+ }
+ acpi_scan_add_handler_with_hotplug(&memory_device_handler, "memory");
}
-static acpi_status
-acpi_memory_deregister_notify_handler(acpi_handle handle,
- u32 level, void *ctxt, void **retv)
+static int __init disable_acpi_memory_hotplug(char *str)
{
- acpi_status status;
-
-
- status = is_memory_device(handle);
- if (ACPI_FAILURE(status))
- return AE_OK; /* continue */
-
- status = acpi_remove_notify_handler(handle,
- ACPI_SYSTEM_NOTIFY,
- acpi_memory_device_notify);
-
- return AE_OK; /* continue */
+ acpi_no_memhotplug = true;
+ return 1;
}
+__setup("acpi_no_memhotplug", disable_acpi_memory_hotplug);
-static int __init acpi_memory_device_init(void)
-{
- int result;
- acpi_status status;
-
-
- result = acpi_bus_register_driver(&acpi_memory_device_driver);
-
- if (result < 0)
- return -ENODEV;
-
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX,
- acpi_memory_register_notify_handler, NULL,
- NULL, NULL);
-
- if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
- acpi_bus_unregister_driver(&acpi_memory_device_driver);
- return -ENODEV;
- }
+#else
- acpi_hotmem_initialized = 1;
- return 0;
-}
+static struct acpi_scan_handler memory_device_handler = {
+ .ids = memory_device_ids,
+};
-static void __exit acpi_memory_device_exit(void)
+void __init acpi_memory_hotplug_init(void)
{
- acpi_status status;
-
-
- /*
- * Adding this to un-install notification handlers for all the device
- * handles.
- */
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX,
- acpi_memory_deregister_notify_handler, NULL,
- NULL, NULL);
-
- if (ACPI_FAILURE(status))
- ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
-
- acpi_bus_unregister_driver(&acpi_memory_device_driver);
-
- return;
+ acpi_scan_add_handler(&memory_device_handler);
}
-module_init(acpi_memory_device_init);
-module_exit(acpi_memory_device_exit);
+#endif /* CONFIG_ACPI_HOTPLUG_MEMORY */