From c56980744ed99994799850903627c4bbb5fed006 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 3 Mar 2013 23:05:14 +0100 Subject: ACPI / scan: Introduce acpi_scan_match_handler() Introduce helper routine acpi_scan_match_handler() that will find the ACPI scan handler matching a given device ID, if there is one, and rework acpi_scan_attach_handler() to use the new routine (that routine will also be useful for other purposes going forward). Signed-off-by: Rafael J. Wysocki Acked-by: Yasuaki Ishimatsu Acked-by: Toshi Kani Tested-by: Toshi Kani --- drivers/acpi/scan.c | 53 ++++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5e7e991717d7..cc1b0020478b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1536,6 +1536,25 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type, return 0; } +static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr, + const struct acpi_device_id **matchid) +{ + struct acpi_scan_handler *handler; + + list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) { + const struct acpi_device_id *devid; + + for (devid = handler->ids; devid->id[0]; devid++) + if (!strcmp((char *)devid->id, idstr)) { + if (matchid) + *matchid = devid; + + return handler; + } + } + return NULL; +} + static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, void *not_used, void **return_value) { @@ -1583,42 +1602,26 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_OK; } -static int acpi_scan_do_attach_handler(struct acpi_device *device, char *id) +static int acpi_scan_attach_handler(struct acpi_device *device) { - struct acpi_scan_handler *handler; + struct acpi_hardware_id *hwid; + int ret = 0; - list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) { + list_for_each_entry(hwid, &device->pnp.ids, list) { const struct acpi_device_id *devid; + struct acpi_scan_handler *handler; - for (devid = handler->ids; devid->id[0]; devid++) { - int ret; - - if (strcmp((char *)devid->id, id)) - continue; - + handler = acpi_scan_match_handler(hwid->id, &devid); + if (handler) { ret = handler->attach(device, devid); if (ret > 0) { device->handler = handler; - return ret; + break; } else if (ret < 0) { - return ret; + break; } } } - return 0; -} - -static int acpi_scan_attach_handler(struct acpi_device *device) -{ - struct acpi_hardware_id *hwid; - int ret = 0; - - list_for_each_entry(hwid, &device->pnp.ids, list) { - ret = acpi_scan_do_attach_handler(device, hwid->id); - if (ret) - break; - - } return ret; } -- cgit v1.2.3 From a33ec399e9fc266ba20f9b71d693aa63658bf2aa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 3 Mar 2013 23:05:29 +0100 Subject: ACPI / scan: Introduce common code for ACPI-based device hotplug Multiple drivers handling hotplug-capable ACPI device nodes install notify handlers covering the same types of events in a very similar way. Moreover, those handlers are installed in separate namespace walks, although that really should be done during namespace scans carried out by acpi_bus_scan(). This leads to substantial code duplication, unnecessary overhead and behavior that is hard to follow. For this reason, introduce common code in drivers/acpi/scan.c for handling hotplug-related notification and carrying out device insertion and eject operations in a generic fashion, such that it may be used by all of the relevant drivers in the future. To cover the existing differences between those drivers introduce struct acpi_hotplug_profile for representing collections of hotplug settings associated with different ACPI scan handlers that can be used by the drivers to make the common code reflect their current behavior. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Tested-by: Toshi Kani --- drivers/acpi/scan.c | 269 ++++++++++++++++++++++++++++++++++++++---------- include/acpi/acpi_bus.h | 12 +++ 2 files changed, 228 insertions(+), 53 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index cc1b0020478b..de73fdf89598 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -107,32 +107,19 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -/** - * acpi_bus_hot_remove_device: hot-remove a device and its children - * @context: struct acpi_eject_event pointer (freed in this func) - * - * Hot-remove a device and its children. This function frees up the - * memory space passed by arg context, so that the caller may call - * this function asynchronously through acpi_os_hotplug_execute(). - */ -void acpi_bus_hot_remove_device(void *context) +static int acpi_scan_hot_remove(struct acpi_device *device) { - struct acpi_eject_event *ej_event = context; - struct acpi_device *device = ej_event->device; acpi_handle handle = device->handle; - acpi_handle temp; + acpi_handle not_used; struct acpi_object_list arg_list; union acpi_object arg; - acpi_status status = AE_OK; - u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ - - mutex_lock(&acpi_scan_lock); + acpi_status status; /* If there is no handle, the device node has been unregistered. */ - if (!device->handle) { + if (!handle) { dev_dbg(&device->dev, "ACPI handle missing\n"); put_device(&device->dev); - goto out; + return -EINVAL; } ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -143,7 +130,7 @@ void acpi_bus_hot_remove_device(void *context) put_device(&device->dev); device = NULL; - if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &temp))) { + if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", ¬_used))) { arg_list.count = 1; arg_list.pointer = &arg; arg.type = ACPI_TYPE_INTEGER; @@ -161,18 +148,158 @@ void acpi_bus_hot_remove_device(void *context) */ status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); if (ACPI_FAILURE(status)) { - if (status != AE_NOT_FOUND) + if (status == AE_NOT_FOUND) { + return -ENODEV; + } else { acpi_handle_warn(handle, "Eject failed\n"); + return -EIO; + } + } + return 0; +} - /* Tell the firmware the hot-remove operation has failed. */ - acpi_evaluate_hotplug_ost(handle, ej_event->event, - ost_code, NULL); +static void acpi_bus_device_eject(void *context) +{ + acpi_handle handle = context; + struct acpi_device *device = NULL; + struct acpi_scan_handler *handler; + u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; + + mutex_lock(&acpi_scan_lock); + + acpi_bus_get_device(handle, &device); + if (!device) + goto err_out; + + handler = device->handler; + if (!handler || !handler->hotplug.enabled) { + ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; + goto err_out; + } + acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, + ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + if (handler->hotplug.mode == AHM_CONTAINER) { + device->flags.eject_pending = true; + kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); + } else { + int error; + + get_device(&device->dev); + error = acpi_scan_hot_remove(device); + if (error) + goto err_out; } out: mutex_unlock(&acpi_scan_lock); - kfree(context); return; + + err_out: + acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, ost_code, + NULL); + goto out; +} + +static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source) +{ + struct acpi_device *device = NULL; + u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; + int error; + + mutex_lock(&acpi_scan_lock); + + acpi_bus_get_device(handle, &device); + if (device) { + dev_warn(&device->dev, "Attempt to re-insert\n"); + goto out; + } + acpi_evaluate_hotplug_ost(handle, ost_source, + ACPI_OST_SC_INSERT_IN_PROGRESS, NULL); + error = acpi_bus_scan(handle); + if (error) { + acpi_handle_warn(handle, "Namespace scan failure\n"); + goto out; + } + error = acpi_bus_get_device(handle, &device); + if (error) { + acpi_handle_warn(handle, "Missing device node object\n"); + goto out; + } + ost_code = ACPI_OST_SC_SUCCESS; + if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER) + kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); + + out: + acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL); + mutex_unlock(&acpi_scan_lock); +} + +static void acpi_scan_bus_check(void *context) +{ + acpi_scan_bus_device_check((acpi_handle)context, + ACPI_NOTIFY_BUS_CHECK); +} + +static void acpi_scan_device_check(void *context) +{ + acpi_scan_bus_device_check((acpi_handle)context, + ACPI_NOTIFY_DEVICE_CHECK); +} + +static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *not_used) +{ + acpi_osd_exec_callback callback; + acpi_status status; + + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); + callback = acpi_scan_bus_check; + break; + case ACPI_NOTIFY_DEVICE_CHECK: + acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n"); + callback = acpi_scan_device_check; + break; + case ACPI_NOTIFY_EJECT_REQUEST: + acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n"); + callback = acpi_bus_device_eject; + break; + default: + /* non-hotplug event; possibly handled by other handler */ + return; + } + status = acpi_os_hotplug_execute(callback, handle); + if (ACPI_FAILURE(status)) + acpi_evaluate_hotplug_ost(handle, type, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, + NULL); +} + +/** + * acpi_bus_hot_remove_device: hot-remove a device and its children + * @context: struct acpi_eject_event pointer (freed in this func) + * + * Hot-remove a device and its children. This function frees up the + * memory space passed by arg context, so that the caller may call + * this function asynchronously through acpi_os_hotplug_execute(). + */ +void acpi_bus_hot_remove_device(void *context) +{ + struct acpi_eject_event *ej_event = context; + struct acpi_device *device = ej_event->device; + acpi_handle handle = device->handle; + int error; + + mutex_lock(&acpi_scan_lock); + + error = acpi_scan_hot_remove(device); + if (error && handle) + acpi_evaluate_hotplug_ost(handle, ej_event->event, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, + NULL); + + mutex_unlock(&acpi_scan_lock); + kfree(context); } EXPORT_SYMBOL(acpi_bus_hot_remove_device); @@ -206,51 +333,61 @@ static ssize_t acpi_eject_store(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int ret = count; - acpi_status status; - acpi_object_type type = 0; struct acpi_device *acpi_device = to_acpi_device(d); struct acpi_eject_event *ej_event; + acpi_object_type not_used; + acpi_status status; + u32 ost_source; + int ret; - if ((!count) || (buf[0] != '1')) { + if (!count || buf[0] != '1') return -EINVAL; - } - if (!acpi_device->driver && !acpi_device->handler) { - ret = -ENODEV; - goto err; - } - status = acpi_get_type(acpi_device->handle, &type); - if (ACPI_FAILURE(status) || (!acpi_device->flags.ejectable)) { - ret = -ENODEV; - goto err; - } - ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); - if (!ej_event) { - ret = -ENOMEM; - goto err; - } + if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled) + && !acpi_device->driver) + return -ENODEV; + + status = acpi_get_type(acpi_device->handle, ¬_used); + if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable) + return -ENODEV; + + mutex_lock(&acpi_scan_lock); - get_device(&acpi_device->dev); - ej_event->device = acpi_device; if (acpi_device->flags.eject_pending) { - /* event originated from ACPI eject notification */ - ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; + /* ACPI eject notification event. */ + ost_source = ACPI_NOTIFY_EJECT_REQUEST; acpi_device->flags.eject_pending = 0; } else { - /* event originated from user */ - ej_event->event = ACPI_OST_EC_OSPM_EJECT; - (void) acpi_evaluate_hotplug_ost(acpi_device->handle, - ej_event->event, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + /* Eject initiated by user space. */ + ost_source = ACPI_OST_EC_OSPM_EJECT; } - + ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); + if (!ej_event) { + ret = -ENOMEM; + goto err_out; + } + acpi_evaluate_hotplug_ost(acpi_device->handle, ost_source, + ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + ej_event->device = acpi_device; + ej_event->event = ost_source; + get_device(&acpi_device->dev); status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event); if (ACPI_FAILURE(status)) { put_device(&acpi_device->dev); kfree(ej_event); + ret = status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN; + goto err_out; } -err: + ret = count; + + out: + mutex_unlock(&acpi_scan_lock); return ret; + + err_out: + acpi_evaluate_hotplug_ost(acpi_device->handle, ost_source, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); + goto out; } static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); @@ -1555,6 +1692,30 @@ static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr, return NULL; } +static void acpi_scan_init_hotplug(acpi_handle handle) +{ + struct acpi_device_info *info; + struct acpi_scan_handler *handler; + + if (ACPI_FAILURE(acpi_get_object_info(handle, &info))) + return; + + if (!(info->valid & ACPI_VALID_HID)) { + kfree(info); + return; + } + + /* + * This relies on the fact that acpi_install_notify_handler() will not + * install the same notify handler routine twice for the same handle. + */ + handler = acpi_scan_match_handler(info->hardware_id.string, NULL); + kfree(info); + if (handler && handler->hotplug.enabled) + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + acpi_hotplug_notify_cb, NULL); +} + static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, void *not_used, void **return_value) { @@ -1577,6 +1738,8 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_OK; } + acpi_scan_init_hotplug(handle); + if (!(sta & ACPI_STA_DEVICE_PRESENT) && !(sta & ACPI_STA_DEVICE_FUNCTIONING)) { struct acpi_device_wakeup wakeup; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index e65278f560c4..f2c1d08a4798 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -88,11 +88,23 @@ struct acpi_device; * ----------------- */ +enum acpi_hotplug_mode { + AHM_GENERIC = 0, + AHM_CONTAINER, + AHM_COUNT +}; + +struct acpi_hotplug_profile { + bool enabled:1; + enum acpi_hotplug_mode mode; +}; + struct acpi_scan_handler { const struct acpi_device_id *ids; struct list_head list_node; int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); void (*detach)(struct acpi_device *dev); + struct acpi_hotplug_profile hotplug; }; /* -- cgit v1.2.3 From 68a67f6c78b80525d9b3c6672e7782de95e56a83 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 3 Mar 2013 23:05:55 +0100 Subject: ACPI / container: Use common hotplug code Switch the ACPI container driver to using common device hotplug code introduced previously. This reduces the driver down to a trivial definition and registration of a struct acpi_scan_handler object. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Tested-by: Toshi Kani --- drivers/acpi/container.c | 146 ++++------------------------------------------- 1 file changed, 10 insertions(+), 136 deletions(-) diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 5523ba7d764d..7f08dd68c524 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -1,12 +1,12 @@ /* - * acpi_container.c - ACPI Generic Container Driver - * ($Revision: ) + * container.c - ACPI Generic Container Driver * * Copyright (C) 2004 Anil S Keshavamurthy (anil.s.keshavamurthy@intel.com) * Copyright (C) 2004 Keiichiro Tokunaga (tokunaga.keiich@jp.fujitsu.com) * Copyright (C) 2004 Motoyuki Ito (motoyuki@soft.fujitsu.com) - * Copyright (C) 2004 Intel Corp. * Copyright (C) 2004 FUJITSU LIMITED + * Copyright (C) 2004, 2013 Intel Corp. + * Author: Rafael J. Wysocki * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -26,14 +26,9 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -#include -#include -#include -#include -#include #include -#include -#include + +#include "internal.h" #define PREFIX "ACPI: " @@ -50,141 +45,20 @@ static const struct acpi_device_id container_device_ids[] = { static int container_device_attach(struct acpi_device *device, const struct acpi_device_id *not_used) { - /* - * FIXME: This is necessary, so that acpi_eject_store() doesn't return - * -ENODEV for containers. - */ + /* This is necessary for container hotplug to work. */ return 1; } static struct acpi_scan_handler container_device_handler = { .ids = container_device_ids, .attach = container_device_attach, + .hotplug = { + .enabled = true, + .mode = AHM_CONTAINER, + }, }; -static int is_device_present(acpi_handle handle) -{ - acpi_handle temp; - acpi_status status; - unsigned long long sta; - - - status = acpi_get_handle(handle, "_STA", &temp); - if (ACPI_FAILURE(status)) - return 1; /* _STA not found, assume device present */ - - status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); - if (ACPI_FAILURE(status)) - return 0; /* Firmware error */ - - return ((sta & ACPI_STA_DEVICE_PRESENT) == ACPI_STA_DEVICE_PRESENT); -} - -static void container_notify_cb(acpi_handle handle, u32 type, void *context) -{ - struct acpi_device *device = NULL; - int result; - int present; - acpi_status status; - u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ - - acpi_scan_lock_acquire(); - - switch (type) { - case ACPI_NOTIFY_BUS_CHECK: - /* Fall through */ - case ACPI_NOTIFY_DEVICE_CHECK: - pr_debug("Container driver received %s event\n", - (type == ACPI_NOTIFY_BUS_CHECK) ? - "ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK"); - - present = is_device_present(handle); - status = acpi_bus_get_device(handle, &device); - if (!present) { - if (ACPI_SUCCESS(status)) { - /* device exist and this is a remove request */ - device->flags.eject_pending = 1; - kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); - goto out; - } - break; - } - - if (!ACPI_FAILURE(status) || device) - break; - - result = acpi_bus_scan(handle); - if (result) { - acpi_handle_warn(handle, "Failed to add container\n"); - break; - } - result = acpi_bus_get_device(handle, &device); - if (result) { - acpi_handle_warn(handle, "Missing device object\n"); - break; - } - - kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); - ost_code = ACPI_OST_SC_SUCCESS; - break; - - case ACPI_NOTIFY_EJECT_REQUEST: - if (!acpi_bus_get_device(handle, &device) && device) { - device->flags.eject_pending = 1; - kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); - goto out; - } - break; - - default: - /* non-hotplug event; possibly handled by other handler */ - goto out; - } - - /* Inform firmware that the hotplug operation has completed */ - (void) acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); - - out: - acpi_scan_lock_release(); -} - -static bool is_container(acpi_handle handle) -{ - struct acpi_device_info *info; - bool ret = false; - - if (ACPI_FAILURE(acpi_get_object_info(handle, &info))) - return false; - - if (info->valid & ACPI_VALID_HID) { - const struct acpi_device_id *id; - - for (id = container_device_ids; id->id[0]; id++) { - ret = !strcmp((char *)id->id, info->hardware_id.string); - if (ret) - break; - } - } - kfree(info); - return ret; -} - -static acpi_status acpi_container_register_notify_handler(acpi_handle handle, - u32 lvl, void *ctxt, - void **retv) -{ - if (is_container(handle)) - acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - container_notify_cb, NULL); - - return AE_OK; -} - void __init acpi_container_init(void) { - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, - acpi_container_register_notify_handler, NULL, - NULL, NULL); - acpi_scan_add_handler(&container_device_handler); } -- cgit v1.2.3 From 4b59cc1fd6fd1dac1d4468b4f327ae9f59d1c0aa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 3 Mar 2013 23:06:21 +0100 Subject: ACPI / scan: Introduce acpi_scan_handler_matching() Introduce new helper routine acpi_scan_handler_matching() for checking if the given ACPI scan handler matches a given device ID and rework acpi_scan_match_handler() to use the new routine (that routine will also be useful for other purposes in the future). Signed-off-by: Rafael J. Wysocki Acked-by: Yasuaki Ishimatsu Acked-by: Toshi Kani Tested-by: Toshi Kani --- drivers/acpi/scan.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index de73fdf89598..45fbe95ba1f3 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1673,22 +1673,32 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type, return 0; } +static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler, + char *idstr, + const struct acpi_device_id **matchid) +{ + const struct acpi_device_id *devid; + + for (devid = handler->ids; devid->id[0]; devid++) + if (!strcmp((char *)devid->id, idstr)) { + if (matchid) + *matchid = devid; + + return true; + } + + return false; +} + static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr, const struct acpi_device_id **matchid) { struct acpi_scan_handler *handler; - list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) { - const struct acpi_device_id *devid; - - for (devid = handler->ids; devid->id[0]; devid++) - if (!strcmp((char *)devid->id, idstr)) { - if (matchid) - *matchid = devid; + list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) + if (acpi_scan_handler_matching(handler, idstr, matchid)) + return handler; - return handler; - } - } return NULL; } -- cgit v1.2.3 From 3f8055c3583640ed3e4c81864dd76e06a7faa505 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 3 Mar 2013 23:08:16 +0100 Subject: ACPI / hotplug: Introduce user space interface for hotplug profiles Introduce user space interface for manipulating hotplug profiles associated with ACPI scan handlers. The interface consists of sysfs directories under /sys/firmware/acpi/hotplug/, one for each hotplug profile, containing an attribute allowing user space to manipulate the enabled field of the corresponding profile. Namely, switching the enabled attribute from '0' to '1' will cause the common hotplug notify handler to be installed for all ACPI namespace objects representing devices matching the scan handler associated with the given hotplug profile (and analogously for the converse switch). Drivers willing to use the new user space interface should add their ACPI scan handlers with the help of new funtion acpi_scan_add_handler_with_hotplug(). Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Tested-by: Toshi Kani --- Documentation/ABI/testing/sysfs-firmware-acpi | 26 +++++++++++ drivers/acpi/internal.h | 6 +++ drivers/acpi/scan.c | 59 ++++++++++++++++++++++++ drivers/acpi/sysfs.c | 66 +++++++++++++++++++++++++++ include/acpi/acpi_bus.h | 7 +++ 5 files changed, 164 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-firmware-acpi b/Documentation/ABI/testing/sysfs-firmware-acpi index dd930c8db41f..ce9bee98b43b 100644 --- a/Documentation/ABI/testing/sysfs-firmware-acpi +++ b/Documentation/ABI/testing/sysfs-firmware-acpi @@ -18,6 +18,32 @@ Description: yoffset: The number of pixels between the top of the screen and the top edge of the image. +What: /sys/firmware/acpi/hotplug/ +Date: February 2013 +Contact: Rafael J. Wysocki +Description: + There are separate hotplug profiles for different classes of + devices supported by ACPI, such as containers, memory modules, + processors, PCI root bridges etc. A hotplug profile for a given + class of devices is a collection of settings defining the way + that class of devices will be handled by the ACPI core hotplug + code. Those profiles are represented in sysfs as subdirectories + of /sys/firmware/acpi/hotplug/. + + The following setting is available to user space for each + hotplug profile: + + enabled: If set, the ACPI core will handle notifications of + hotplug events associated with the given class of + devices and will allow those devices to be ejected with + the help of the _EJ0 control method. Unsetting it + effectively disables hotplug for the correspoinding + class of devices. + + The value of the above attribute is an integer number: 1 (set) + or 0 (unset). Attempts to write any other values to it will + cause -EINVAL to be returned. + What: /sys/firmware/acpi/interrupts/ Date: February 2008 Contact: Len Brown diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 3c94a732b4b3..c708e4bad967 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -42,6 +42,12 @@ void acpi_container_init(void); static inline void acpi_container_init(void) {} #endif +void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, + const char *name); +int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, + const char *hotplug_profile_name); +void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val); + #ifdef CONFIG_DEBUG_FS extern struct dentry *acpi_debugfs_dir; int acpi_debugfs_init(void); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 45fbe95ba1f3..5458403c8249 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -63,6 +63,19 @@ int acpi_scan_add_handler(struct acpi_scan_handler *handler) return 0; } +int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, + const char *hotplug_profile_name) +{ + int error; + + error = acpi_scan_add_handler(handler); + if (error) + return error; + + acpi_sysfs_add_hotplug_profile(&handler->hotplug, hotplug_profile_name); + return 0; +} + /* * Creates hid/cid(s) string needed for modalias and uevent * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: @@ -1690,6 +1703,52 @@ static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler, return false; } +static acpi_status acpi_scan_hotplug_modify(acpi_handle handle, + u32 lvl_not_used, void *data, + void **ret_not_used) +{ + struct acpi_scan_handler *handler = data; + struct acpi_device_info *info; + bool match = false; + + if (ACPI_FAILURE(acpi_get_object_info(handle, &info))) + return AE_OK; + + if (info->valid & ACPI_VALID_HID) { + char *idstr = info->hardware_id.string; + match = acpi_scan_handler_matching(handler, idstr, NULL); + } + kfree(info); + if (!match) + return AE_OK; + + if (handler->hotplug.enabled) + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + acpi_hotplug_notify_cb, NULL); + else + acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + acpi_hotplug_notify_cb); + + return AE_OK; +} + +void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val) +{ + struct acpi_scan_handler *handler; + + if (!!hotplug->enabled == !!val) + return; + + mutex_lock(&acpi_scan_lock); + + hotplug->enabled = val; + handler = container_of(hotplug, struct acpi_scan_handler, hotplug); + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, + acpi_scan_hotplug_modify, NULL, handler, NULL); + + mutex_unlock(&acpi_scan_lock); +} + static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr, const struct acpi_device_id **matchid) { diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 41c0504470db..83db3a68a7ee 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -7,6 +7,8 @@ #include #include +#include "internal.h" + #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME("sysfs"); @@ -249,6 +251,7 @@ module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444); static LIST_HEAD(acpi_table_attr_list); static struct kobject *tables_kobj; static struct kobject *dynamic_tables_kobj; +static struct kobject *hotplug_kobj; struct acpi_table_attr { struct bin_attribute attr; @@ -716,6 +719,67 @@ acpi_show_profile(struct device *dev, struct device_attribute *attr, static const struct device_attribute pm_profile_attr = __ATTR(pm_profile, S_IRUGO, acpi_show_profile, NULL); +static ssize_t hotplug_enabled_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj); + + return sprintf(buf, "%d\n", hotplug->enabled); +} + +static ssize_t hotplug_enabled_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t size) +{ + struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj); + unsigned int val; + + if (kstrtouint(buf, 10, &val) || val > 1) + return -EINVAL; + + acpi_scan_hotplug_enabled(hotplug, val); + return size; +} + +static struct kobj_attribute hotplug_enabled_attr = + __ATTR(enabled, S_IRUGO | S_IWUSR, hotplug_enabled_show, + hotplug_enabled_store); + +static struct attribute *hotplug_profile_attrs[] = { + &hotplug_enabled_attr.attr, + NULL +}; + +struct kobj_type acpi_hotplug_profile_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .default_attrs = hotplug_profile_attrs, +}; + +void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, + const char *name) +{ + int error; + + if (!hotplug_kobj) + goto err_out; + + kobject_init(&hotplug->kobj, &acpi_hotplug_profile_ktype); + error = kobject_set_name(&hotplug->kobj, "%s", name); + if (error) + goto err_out; + + hotplug->kobj.parent = hotplug_kobj; + error = kobject_add(&hotplug->kobj, hotplug_kobj, NULL); + if (error) + goto err_out; + + kobject_uevent(&hotplug->kobj, KOBJ_ADD); + return; + + err_out: + pr_err(PREFIX "Unable to add hotplug profile '%s'\n", name); +} + int __init acpi_sysfs_init(void) { int result; @@ -723,6 +787,8 @@ int __init acpi_sysfs_init(void) result = acpi_tables_sysfs_init(); if (result) return result; + + hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj); result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr); return result; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index f2c1d08a4798..533ef039c5e0 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -95,10 +95,17 @@ enum acpi_hotplug_mode { }; struct acpi_hotplug_profile { + struct kobject kobj; bool enabled:1; enum acpi_hotplug_mode mode; }; +static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile( + struct kobject *kobj) +{ + return container_of(kobj, struct acpi_hotplug_profile, kobj); +} + struct acpi_scan_handler { const struct acpi_device_id *ids; struct list_head list_node; -- cgit v1.2.3 From 79917f34ac83140c20b06303b608ce6d740f0266 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 3 Mar 2013 23:08:45 +0100 Subject: ACPI / container: Use hotplug profile user space interface Make the ACPI container driver register its ACPI scan handler object using acpi_scan_add_handler_with_hotplug() to allow user space to manipulate its hotplug profile attributes. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Tested-by: Toshi Kani --- drivers/acpi/container.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 7f08dd68c524..f9f8a08827fa 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -49,7 +49,7 @@ static int container_device_attach(struct acpi_device *device, return 1; } -static struct acpi_scan_handler container_device_handler = { +static struct acpi_scan_handler container_handler = { .ids = container_device_ids, .attach = container_device_attach, .hotplug = { @@ -60,5 +60,5 @@ static struct acpi_scan_handler container_device_handler = { void __init acpi_container_init(void) { - acpi_scan_add_handler(&container_device_handler); + acpi_scan_add_handler_with_hotplug(&container_handler, "container"); } -- cgit v1.2.3 From 0a34764411aaab0114aa3f3656fda33a69a46d10 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 3 Mar 2013 23:18:03 +0100 Subject: ACPI / scan: Make memory hotplug driver use struct acpi_scan_handler Make the ACPI memory hotplug driver use struct acpi_scan_handler for representing the object used to set up ACPI memory hotplug functionality and to remove hotplug memory ranges and data structures used by the driver before unregistering ACPI device nodes representing memory. Register the new struct acpi_scan_handler object with the help of acpi_scan_add_handler_with_hotplug() to allow user space to manipulate the attributes of the memory hotplug profile. This results in a significant reduction of the drvier's code size and removes some ACPI hotplug code duplication. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Tested-by: Toshi Kani --- drivers/acpi/Kconfig | 3 +- drivers/acpi/acpi_memhotplug.c | 309 +++++------------------------------------ drivers/acpi/internal.h | 5 + drivers/acpi/scan.c | 1 + 4 files changed, 45 insertions(+), 273 deletions(-) diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 92ed9692c47e..da8082391f97 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -345,9 +345,8 @@ config ACPI_CONTAINER the module will be called container. config ACPI_HOTPLUG_MEMORY - tristate "Memory Hotplug" + bool "Memory Hotplug" depends on MEMORY_HOTPLUG - default n help This driver supports ACPI memory hotplug. The driver fields notifications on ACPI memory devices (PNP0C80), diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index da1f82b445e0..d4f2eb8a51ac 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -1,5 +1,7 @@ /* - * Copyright (C) 2004 Intel Corporation + * Copyright (C) 2004, 2013 Intel Corporation + * Author: Naveen B S + * Author: Rafael J. Wysocki * * All rights reserved. * @@ -25,14 +27,10 @@ * ranges. */ -#include -#include -#include -#include -#include -#include #include -#include +#include + +#include "internal.h" #define ACPI_MEMORY_DEVICE_CLASS "memory" #define ACPI_MEMORY_DEVICE_HID "PNP0C80" @@ -44,32 +42,28 @@ #define PREFIX "ACPI:memory_hp:" ACPI_MODULE_NAME("acpi_memhotplug"); -MODULE_AUTHOR("Naveen B S "); -MODULE_DESCRIPTION("Hotplug Mem Driver"); -MODULE_LICENSE("GPL"); /* 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); +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 { @@ -153,48 +147,6 @@ acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) return 0; } -static int acpi_memory_get_device(acpi_handle handle, - struct acpi_memory_device **mem_device) -{ - struct acpi_device *device = NULL; - int result = 0; - - acpi_scan_lock_acquire(); - - acpi_bus_get_device(handle, &device); - if (device) - goto end; - - /* - * Now add the notified device. This creates the acpi_device - * and invokes .add function - */ - result = acpi_bus_scan(handle); - if (result) { - acpi_handle_warn(handle, "ACPI namespace scan failed\n"); - result = -EINVAL; - goto out; - } - result = acpi_bus_get_device(handle, &device); - if (result) { - acpi_handle_warn(handle, "Missing device object\n"); - result = -EINVAL; - goto out; - } - - end: - *mem_device = acpi_driver_data(device); - if (!(*mem_device)) { - dev_err(&device->dev, "driver data not found\n"); - result = -ENODEV; - goto out; - } - - out: - acpi_scan_lock_release(); - return result; -} - static int acpi_memory_check_device(struct acpi_memory_device *mem_device) { unsigned long long current_status; @@ -310,95 +262,21 @@ static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) return result; } -static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) -{ - struct acpi_memory_device *mem_device; - struct acpi_device *device; - struct acpi_eject_event *ej_event = NULL; - u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ - acpi_status status; - - 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)) { - acpi_handle_err(handle, "Cannot find driver data\n"); - break; - } - - ost_code = ACPI_OST_SC_SUCCESS; - break; - - case ACPI_NOTIFY_EJECT_REQUEST: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "\nReceived EJECT REQUEST notification for device\n")); - - status = AE_ERROR; - acpi_scan_lock_acquire(); - - if (acpi_bus_get_device(handle, &device)) { - acpi_handle_err(handle, "Device doesn't exist\n"); - goto unlock; - } - mem_device = acpi_driver_data(device); - if (!mem_device) { - acpi_handle_err(handle, "Driver Data is NULL\n"); - goto unlock; - } - - ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); - if (!ej_event) { - pr_err(PREFIX "No memory, dropping EJECT\n"); - goto unlock; - } - - get_device(&device->dev); - ej_event->device = device; - ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; - /* The eject is carried out asynchronously. */ - status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, - ej_event); - if (ACPI_FAILURE(status)) { - put_device(&device->dev); - kfree(ej_event); - } - - unlock: - acpi_scan_lock_release(); - if (ACPI_SUCCESS(status)) - return; - default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); - - /* non-hotplug event; possibly handled by other handler */ - return; - } - - /* Inform firmware that the hotplug operation has completed */ - (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL); -} - static void acpi_memory_device_free(struct acpi_memory_device *mem_device) { if (!mem_device) 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; @@ -423,147 +301,36 @@ static int acpi_memory_device_add(struct acpi_device *device) /* Set the device state */ mem_device->state = MEMORY_POWER_ON_STATE; - pr_debug("%s\n", acpi_device_name(device)); + 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) { - dev_err(&device->dev, - "Error in acpi_memory_enable_device\n"); - acpi_memory_device_free(mem_device); - } + 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 -ENODEV; } - return result; + + dev_dbg(&device->dev, "Memory device configured by ACPI\n"); + return 1; } -static int acpi_memory_device_remove(struct acpi_device *device) +static void acpi_memory_device_remove(struct acpi_device *device) { - struct acpi_memory_device *mem_device = NULL; - int result; + struct acpi_memory_device *mem_device; if (!device || !acpi_driver_data(device)) - return -EINVAL; + return; mem_device = acpi_driver_data(device); - - result = acpi_memory_remove_memory(mem_device); - if (result) - return result; - + acpi_memory_remove_memory(mem_device); acpi_memory_device_free(mem_device); - - return 0; -} - -/* - * 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; - - kfree(info); - return status; -} - -static acpi_status -acpi_memory_register_notify_handler(acpi_handle handle, - u32 level, void *ctxt, void **retv) -{ - 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; -} - -static acpi_status -acpi_memory_deregister_notify_handler(acpi_handle handle, - u32 level, void *ctxt, void **retv) -{ - 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 */ } -static int __init acpi_memory_device_init(void) +void __init acpi_memory_hotplug_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; - } - - return 0; -} - -static void __exit acpi_memory_device_exit(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_with_hotplug(&memory_device_handler, "memory"); } - -module_init(acpi_memory_device_init); -module_exit(acpi_memory_device_exit); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index c708e4bad967..7215821ccb25 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -41,6 +41,11 @@ void acpi_container_init(void); #else static inline void acpi_container_init(void) {} #endif +#ifdef CONFIG_ACPI_HOTPLUG_MEMORY +void acpi_memory_hotplug_init(void); +#else +static inline void acpi_memory_hotplug_init(void) {} +#endif void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, const char *name); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5458403c8249..d69d77ab9c7e 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2026,6 +2026,7 @@ int __init acpi_scan_init(void) acpi_csrt_init(); acpi_container_init(); acpi_pci_slot_init(); + acpi_memory_hotplug_init(); mutex_lock(&acpi_scan_lock); /* -- cgit v1.2.3 From c13085e519e8984fede41fa3d6a5502523b10996 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 8 Mar 2013 09:19:38 +0000 Subject: ACPICA: Resource Mgr: Prevent infinite loops in resource walks Add checks for zero-length resource descriptors in all code that loops through a resource descriptor list. This prevents possible infinite loops because the length is used to increment the traveral pointer and detect the end-of-descriptor. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/rscalc.c | 6 ++++++ drivers/acpi/acpica/rsdump.c | 8 ++++++++ drivers/acpi/acpica/rslist.c | 8 ++++++++ drivers/acpi/acpica/rsxface.c | 8 +++++++- 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c index 7816d4eef04e..72077fa1eea5 100644 --- a/drivers/acpi/acpica/rscalc.c +++ b/drivers/acpi/acpica/rscalc.c @@ -202,6 +202,12 @@ acpi_rs_get_aml_length(struct acpi_resource * resource, acpi_size * size_needed) return_ACPI_STATUS(AE_AML_INVALID_RESOURCE_TYPE); } + /* Sanity check the length. It must not be zero, or we loop forever */ + + if (!resource->length) { + return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH); + } + /* Get the base size of the (external stream) resource descriptor */ total_size = acpi_gbl_aml_resource_sizes[resource->type]; diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c index cab51445189d..b5fc0db2e87b 100644 --- a/drivers/acpi/acpica/rsdump.c +++ b/drivers/acpi/acpica/rsdump.c @@ -385,6 +385,14 @@ void acpi_rs_dump_resource_list(struct acpi_resource *resource_list) return; } + /* Sanity check the length. It must not be zero, or we loop forever */ + + if (!resource_list->length) { + acpi_os_printf + ("Invalid zero length descriptor in resource list\n"); + return; + } + /* Dump the resource descriptor */ if (type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { diff --git a/drivers/acpi/acpica/rslist.c b/drivers/acpi/acpica/rslist.c index ee2e206fc6c8..6053aa182093 100644 --- a/drivers/acpi/acpica/rslist.c +++ b/drivers/acpi/acpica/rslist.c @@ -178,6 +178,14 @@ acpi_rs_convert_resources_to_aml(struct acpi_resource *resource, return_ACPI_STATUS(AE_BAD_DATA); } + /* Sanity check the length. It must not be zero, or we loop forever */ + + if (!resource->length) { + ACPI_ERROR((AE_INFO, + "Invalid zero length descriptor in resource list\n")); + return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH); + } + /* Perform the conversion */ if (resource->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c index 15d6eaef0e28..c0e5d2d3ce67 100644 --- a/drivers/acpi/acpica/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -563,13 +563,19 @@ acpi_walk_resource_buffer(struct acpi_buffer * buffer, while (resource < resource_end) { - /* Sanity check the resource */ + /* Sanity check the resource type */ if (resource->type > ACPI_RESOURCE_TYPE_MAX) { status = AE_AML_INVALID_RESOURCE_TYPE; break; } + /* Sanity check the length. It must not be zero, or we loop forever */ + + if (!resource->length) { + return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH); + } + /* Invoke the user function, abort on any error returned */ status = user_function(resource, context); -- cgit v1.2.3 From 3cfcf50ba4b8ef34258464f529e09c3e4324b8d6 Mon Sep 17 00:00:00 2001 From: Jung-uk Kim Date: Fri, 8 Mar 2013 09:20:04 +0000 Subject: ACPICA: Fix a couple warnings detected on FreeBSD build This fixes a global and a pointer cast. Jung-uk Kim. Signed-off-by: Jung-uk Kim Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acglobal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index ecb49927b817..feaa5d5ef579 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -413,7 +413,7 @@ ACPI_EXTERN u8 acpi_gbl_db_output_flags; #ifdef ACPI_DISASSEMBLER -u8 ACPI_INIT_GLOBAL(acpi_gbl_ignore_noop_operator, FALSE); +ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_ignore_noop_operator, FALSE); ACPI_EXTERN u8 acpi_gbl_db_opt_disasm; ACPI_EXTERN u8 acpi_gbl_db_opt_verbose; -- cgit v1.2.3 From 8c2809144a372284f757bd4d70b08206e20681b7 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 8 Mar 2013 09:20:21 +0000 Subject: ACPICA: Update RASF table definition Update to reflect final ACPI 5.0 changes. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl3.h | 51 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index 332b17e3bec8..9f27890d33ad 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -505,26 +505,59 @@ struct acpi_rasf_shared_memory { u32 signature; u16 command; u16 status; - u64 requested_address; - u64 requested_length; - u64 actual_address; - u64 actual_length; + u16 version; + u8 capabilities[16]; + u8 set_capabilities[16]; + u16 num_parameter_blocks; + u32 set_capabilities_status; +}; + +/* RASF Parameter Block Structure Header */ + +struct acpi_rasf_parameter_block { + u16 type; + u16 version; + u16 length; +}; + +/* RASF Parameter Block Structure for PATROL_SCRUB */ + +struct acpi_rasf_patrol_scrub_parameter { + struct acpi_rasf_parameter_block header; + u16 patrol_scrub_command; + u64 requested_address_range[2]; + u64 actual_address_range[2]; u16 flags; - u8 speed; + u8 requested_speed; }; /* Masks for Flags and Speed fields above */ #define ACPI_RASF_SCRUBBER_RUNNING 1 #define ACPI_RASF_SPEED (7<<1) +#define ACPI_RASF_SPEED_SLOW (0<<1) +#define ACPI_RASF_SPEED_MEDIUM (4<<1) +#define ACPI_RASF_SPEED_FAST (7<<1) /* Channel Commands */ enum acpi_rasf_commands { - ACPI_RASF_GET_RAS_CAPABILITIES = 1, - ACPI_RASF_GET_PATROL_PARAMETERS = 2, - ACPI_RASF_START_PATROL_SCRUBBER = 3, - ACPI_RASF_STOP_PATROL_SCRUBBER = 4 + ACPI_RASF_EXECUTE_RASF_COMMAND = 1 +}; + +/* Platform RAS Capabilities */ + +enum acpi_rasf_capabiliities { + ACPI_HW_PATROL_SCRUB_SUPPORTED = 0, + ACPI_SW_PATROL_SCRUB_EXPOSED = 1 +}; + +/* Patrol Scrub Commands */ + +enum acpi_rasf_patrol_scrub_commands { + ACPI_RASF_GET_PATROL_PARAMETERS = 1, + ACPI_RASF_START_PATROL_SCRUBBER = 2, + ACPI_RASF_STOP_PATROL_SCRUBBER = 3 }; /* Channel Command flags */ -- cgit v1.2.3 From 25c0330aa6bba60e36ac460d12ef954332852426 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 8 Mar 2013 09:20:32 +0000 Subject: ACPICA: iASL/Disassembler: Add support for VRTC table VRTC is used in Intel MID platforms as a replacement of the traditional x86 RTC. VRTC table can be found in the recent ACPI BIOS enabled Intel MID platforms. The format of this table has been defined in the "Simple Firmware Interface Specification" except it uses GAS instead of 64-bit values for address fields. This patch introduces VRTC table support into ACPICA. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl2.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 77dc7a4099a3..3f1b0a474ae6 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -77,6 +77,7 @@ #define ACPI_SIG_SPMI "SPMI" /* Server Platform Management Interface table */ #define ACPI_SIG_TCPA "TCPA" /* Trusted Computing Platform Alliance table */ #define ACPI_SIG_UEFI "UEFI" /* Uefi Boot Optimization Table */ +#define ACPI_SIG_VRTC "VRTC" /* Virtual Real Time Clock Table */ #define ACPI_SIG_WAET "WAET" /* Windows ACPI Emulated devices Table */ #define ACPI_SIG_WDAT "WDAT" /* Watchdog Action Table */ #define ACPI_SIG_WDDT "WDDT" /* Watchdog Timer Description Table */ @@ -1023,6 +1024,28 @@ struct acpi_table_uefi { u16 data_offset; /* Offset of remaining data in table */ }; +/******************************************************************************* + * + * VRTC - Virtual Real Time Clock Table + * Version 1 + * + * Conforms to "Simple Firmware Interface Specification", + * Draft 0.8.2, Oct 19, 2010 + * NOTE: The ACPI VRTC is equivalent to The SFI MRTC table. + * + ******************************************************************************/ + +struct acpi_table_vrtc { + struct acpi_table_header header; /* Common ACPI table header */ +}; + +/* VRTC entry */ + +struct acpi_vrtc_entry { + struct acpi_generic_address physical_address; + u32 irq; +}; + /******************************************************************************* * * WAET - Windows ACPI Emulated devices Table -- cgit v1.2.3 From 98b5c9934ccdf6c04413e0d03c1ddeb32592d8c6 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 8 Mar 2013 09:20:45 +0000 Subject: ACPICA: iASL/Disassembler: Add support for MTMR table MTMR table is used in the recent ACPI BIOS enabled Intel MID platforms. The format of this table has been defined in the "Simple Firmware Interface Specification" except it uses GAS instead of 64-bit values for address fields. This patch introduces MTMR table support into ACPICA. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl2.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 3f1b0a474ae6..ffaac0e7e0c6 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -72,6 +72,7 @@ #define ACPI_SIG_IVRS "IVRS" /* I/O Virtualization Reporting Structure */ #define ACPI_SIG_MCFG "MCFG" /* PCI Memory Mapped Configuration table */ #define ACPI_SIG_MCHI "MCHI" /* Management Controller Host Interface table */ +#define ACPI_SIG_MTMR "MTMR" /* MID Timer table */ #define ACPI_SIG_SLIC "SLIC" /* Software Licensing Description Table */ #define ACPI_SIG_SPCR "SPCR" /* Serial Port Console Redirection table */ #define ACPI_SIG_SPMI "SPMI" /* Server Platform Management Interface table */ @@ -851,6 +852,29 @@ struct acpi_table_mchi { u8 pci_function; }; +/******************************************************************************* + * + * MTMR - MID Timer Table + * Version 1 + * + * Conforms to "Simple Firmware Interface Specification", + * Draft 0.8.2, Oct 19, 2010 + * NOTE: The ACPI MTMR is equivalent to the SFI MTMR table. + * + ******************************************************************************/ + +struct acpi_table_mtmr { + struct acpi_table_header header; /* Common ACPI table header */ +}; + +/* MTMR entry */ + +struct acpi_mtmr_entry { + struct acpi_generic_address physical_address; + u32 frequency; + u32 irq; +}; + /******************************************************************************* * * SLIC - Software Licensing Description Table -- cgit v1.2.3 From 3cf24497f45d61ed3c3290b5b03f3baeb8401f04 Mon Sep 17 00:00:00 2001 From: Jung-uk Kim Date: Fri, 8 Mar 2013 09:21:02 +0000 Subject: ACPICA: Fix a long-standing bug in local cache Since 20060317, the pointer to next object is the first element in its common header. Remove bogus LinkOffset from ACPI_MEMORY_LIST and directly use NextObject. Signed-off-by: Jung-uk Kim Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/utcache.c | 20 +++++++------------- include/acpi/actypes.h | 1 - 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/acpi/acpica/utcache.c b/drivers/acpi/acpica/utcache.c index e0e8579deaac..2de22fbacf4b 100644 --- a/drivers/acpi/acpica/utcache.c +++ b/drivers/acpi/acpica/utcache.c @@ -85,7 +85,6 @@ acpi_os_create_cache(char *cache_name, /* Populate the cache object and return it */ ACPI_MEMSET(cache, 0, sizeof(struct acpi_memory_list)); - cache->link_offset = 8; cache->list_name = cache_name; cache->object_size = object_size; cache->max_depth = max_depth; @@ -108,7 +107,7 @@ acpi_os_create_cache(char *cache_name, acpi_status acpi_os_purge_cache(struct acpi_memory_list * cache) { - char *next; + void *next; acpi_status status; ACPI_FUNCTION_ENTRY(); @@ -128,10 +127,9 @@ acpi_status acpi_os_purge_cache(struct acpi_memory_list * cache) /* Delete and unlink one cached state object */ - next = *(ACPI_CAST_INDIRECT_PTR(char, - &(((char *)cache-> - list_head)[cache-> - link_offset]))); + next = + ((struct acpi_object_common *)cache->list_head)-> + next_object; ACPI_FREE(cache->list_head); cache->list_head = next; @@ -221,9 +219,7 @@ acpi_os_release_object(struct acpi_memory_list * cache, void *object) /* Put the object at the head of the cache list */ - *(ACPI_CAST_INDIRECT_PTR(char, - &(((char *)object)[cache-> - link_offset]))) = + ((struct acpi_object_common *)object)->next_object = cache->list_head; cache->list_head = object; cache->current_depth++; @@ -272,10 +268,8 @@ void *acpi_os_acquire_object(struct acpi_memory_list *cache) /* There is an object available, use it */ object = cache->list_head; - cache->list_head = *(ACPI_CAST_INDIRECT_PTR(char, - &(((char *) - object)[cache-> - link_offset]))); + cache->list_head = + ((struct acpi_object_common *)object)->next_object; cache->current_depth--; diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 845e75f1ffd8..3fac1be2d8b4 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -1128,7 +1128,6 @@ struct acpi_memory_list { u16 object_size; u16 max_depth; u16 current_depth; - u16 link_offset; #ifdef ACPI_DBG_TRACK_ALLOCATIONS -- cgit v1.2.3 From d4d32195ff070acca43577250adee6b210decdd3 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 8 Mar 2013 09:21:41 +0000 Subject: ACPICA: Update error/debug messages for fixed events Add the actual fixed event name to all messages for clarity. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/evevent.c | 12 +++++++----- drivers/acpi/acpica/evxface.c | 21 ++++++++++++--------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c index b8ea0b26cde3..02219ffba8b6 100644 --- a/drivers/acpi/acpica/evevent.c +++ b/drivers/acpi/acpica/evevent.c @@ -257,6 +257,8 @@ u32 acpi_ev_fixed_event_detect(void) * * DESCRIPTION: Clears the status bit for the requested event, calls the * handler that previously registered for the event. + * NOTE: If there is no handler for the event, the event is + * disabled to prevent futher interrupts. * ******************************************************************************/ @@ -271,17 +273,17 @@ static u32 acpi_ev_fixed_event_dispatch(u32 event) status_register_id, ACPI_CLEAR_STATUS); /* - * Make sure we've got a handler. If not, report an error. The event is - * disabled to prevent further interrupts. + * Make sure that a handler exists. If not, report an error + * and disable the event to prevent further interrupts. */ - if (NULL == acpi_gbl_fixed_event_handlers[event].handler) { + if (!acpi_gbl_fixed_event_handlers[event].handler) { (void)acpi_write_bit_register(acpi_gbl_fixed_event_info[event]. enable_register_id, ACPI_DISABLE_EVENT); ACPI_ERROR((AE_INFO, - "No installed handler for fixed event [0x%08X]", - event)); + "No installed handler for fixed event - %s (%u), disabling", + acpi_ut_get_event_name(event), event)); return (ACPI_INTERRUPT_NOT_HANDLED); } diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index ddffd6847914..ca5fba99c33b 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -467,9 +467,9 @@ acpi_install_fixed_event_handler(u32 event, return_ACPI_STATUS(status); } - /* Don't allow two handlers. */ + /* Do not allow multiple handlers */ - if (NULL != acpi_gbl_fixed_event_handlers[event].handler) { + if (acpi_gbl_fixed_event_handlers[event].handler) { status = AE_ALREADY_EXISTS; goto cleanup; } @@ -483,8 +483,9 @@ acpi_install_fixed_event_handler(u32 event, if (ACPI_SUCCESS(status)) status = acpi_enable_event(event, 0); if (ACPI_FAILURE(status)) { - ACPI_WARNING((AE_INFO, "Could not enable fixed event 0x%X", - event)); + ACPI_WARNING((AE_INFO, + "Could not enable fixed event - %s (%u)", + acpi_ut_get_event_name(event), event)); /* Remove the handler */ @@ -492,7 +493,8 @@ acpi_install_fixed_event_handler(u32 event, acpi_gbl_fixed_event_handlers[event].context = NULL; } else { ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Enabled fixed event %X, Handler=%p\n", event, + "Enabled fixed event %s (%X), Handler=%p\n", + acpi_ut_get_event_name(event), event, handler)); } @@ -544,11 +546,12 @@ acpi_remove_fixed_event_handler(u32 event, acpi_event_handler handler) if (ACPI_FAILURE(status)) { ACPI_WARNING((AE_INFO, - "Could not write to fixed event enable register 0x%X", - event)); + "Could not disable fixed event - %s (%u)", + acpi_ut_get_event_name(event), event)); } else { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Disabled fixed event %X\n", - event)); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Disabled fixed event - %s (%X)\n", + acpi_ut_get_event_name(event), event)); } (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); -- cgit v1.2.3 From 1af89271711b23ff9eee66de018bc92eb347ea68 Mon Sep 17 00:00:00 2001 From: Jung-uk Kim Date: Fri, 8 Mar 2013 09:21:54 +0000 Subject: ACPICA: Add macros to access pointer to next object in the descriptor list Signed-off-by: Jung-uk Kim Signed-off-by: Bob Moore Signed-