diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2010-02-17 23:42:59 +0100 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2010-02-22 16:20:56 -0800 |
commit | 3f0be67188c60ebf1b5d00354b44b4b24f5af313 (patch) | |
tree | e785e4cd32fc6ea96ecd1cb14c9cd235efbdf4c5 /drivers | |
parent | f517709d65beed95f52f021b43e3035b52ef791a (diff) | |
download | linux-3f0be67188c60ebf1b5d00354b44b4b24f5af313.tar.bz2 |
ACPI / ACPICA: Multiple system notify handlers per device
Currently it only is possible to install one system notify handler
per namespace node, but this is not enough for PCI run-time power
management, because we need to install power management notifiers for
devices that already have hotplug notifiers installed. While in
principle this could be handled at the PCI level, that would be
suboptimal due to the way in which the ACPI-based PCI hotplug code is
designed.
For this reason, modify ACPICA so that it is possible to install more
than one system notify handler per namespace node. Namely, make
acpi_install_notify_handler(), acpi_remove_notify_handler() and
acpi_ev_notify_dispatch() use a list of system notify handler objects
associated with a namespace node.
Make acpi_remove_notify_handler() call acpi_os_wait_events_complete()
upfront to avoid a situation in which concurrent instance of
acpi_remove_notify_handler() removes the handler from under us while
we're waiting for the event queues to flush.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/acpica/acobject.h | 2 | ||||
-rw-r--r-- | drivers/acpi/acpica/evmisc.c | 12 | ||||
-rw-r--r-- | drivers/acpi/acpica/evxface.c | 175 |
3 files changed, 150 insertions, 39 deletions
diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index 64062b1be3ee..07f6e2ea2ee5 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -287,8 +287,10 @@ struct acpi_object_buffer_field { struct acpi_object_notify_handler { ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Parent device */ + u32 handler_type; acpi_notify_handler handler; void *context; + struct acpi_object_notify_handler *next; }; struct acpi_object_addr_handler { diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c index ce224e1eaa89..8f0fac6c4366 100644 --- a/drivers/acpi/acpica/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -259,9 +259,15 @@ static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context) handler_obj = notify_info->notify.handler_obj; if (handler_obj) { - handler_obj->notify.handler(notify_info->notify.node, - notify_info->notify.value, - handler_obj->notify.context); + struct acpi_object_notify_handler *notifier; + + notifier = &handler_obj->notify; + while (notifier) { + notifier->handler(notify_info->notify.node, + notify_info->notify.value, + notifier->context); + notifier = notifier->next; + } } /* All done with the info object */ diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 166cbfe1c706..474e2cab603d 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -218,6 +218,72 @@ ACPI_EXPORT_SYMBOL(acpi_remove_fixed_event_handler) /******************************************************************************* * + * FUNCTION: acpi_populate_handler_object + * + * PARAMETERS: handler_obj - Handler object to populate + * handler_type - The type of handler: + * ACPI_SYSTEM_NOTIFY: system_handler (00-7f) + * ACPI_DEVICE_NOTIFY: driver_handler (80-ff) + * ACPI_ALL_NOTIFY: both system and device + * handler - Address of the handler + * context - Value passed to the handler on each GPE + * next - Address of a handler object to link to + * + * RETURN: None + * + * DESCRIPTION: Populate a handler object. + * + ******************************************************************************/ +static void +acpi_populate_handler_object(struct acpi_object_notify_handler *handler_obj, + u32 handler_type, + acpi_notify_handler handler, void *context, + struct acpi_object_notify_handler *next) +{ + handler_obj->handler_type = handler_type; + handler_obj->handler = handler; + handler_obj->context = context; + handler_obj->next = next; +} + +/******************************************************************************* + * + * FUNCTION: acpi_add_handler_object + * + * PARAMETERS: parent_obj - Parent of the new object + * handler - Address of the handler + * context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Create a new handler object and populate it. + * + ******************************************************************************/ +static acpi_status +acpi_add_handler_object(struct acpi_object_notify_handler *parent_obj, + acpi_notify_handler handler, void *context) +{ + struct acpi_object_notify_handler *handler_obj; + + /* The parent must not be a defice notify handler object. */ + if (parent_obj->handler_type & ACPI_DEVICE_NOTIFY) + return AE_BAD_PARAMETER; + + handler_obj = ACPI_ALLOCATE_ZEROED(sizeof(*handler_obj)); + if (!handler_obj) + return AE_NO_MEMORY; + + acpi_populate_handler_object(handler_obj, + ACPI_SYSTEM_NOTIFY, + handler, context, + parent_obj->next); + parent_obj->next = handler_obj; + + return AE_OK; +} + +/******************************************************************************* + * * FUNCTION: acpi_install_notify_handler * * PARAMETERS: Device - The device for which notifies will be handled @@ -316,15 +382,32 @@ acpi_install_notify_handler(acpi_handle device, obj_desc = acpi_ns_get_attached_object(node); if (obj_desc) { - /* Object exists - make sure there's no handler */ + /* Object exists. */ - if (((handler_type & ACPI_SYSTEM_NOTIFY) && - obj_desc->common_notify.system_notify) || - ((handler_type & ACPI_DEVICE_NOTIFY) && - obj_desc->common_notify.device_notify)) { + /* For a device notify, make sure there's no handler. */ + if ((handler_type & ACPI_DEVICE_NOTIFY) && + obj_desc->common_notify.device_notify) { status = AE_ALREADY_EXISTS; goto unlock_and_exit; } + + /* System notifies may have more handlers installed. */ + notify_obj = obj_desc->common_notify.system_notify; + + if ((handler_type & ACPI_SYSTEM_NOTIFY) && notify_obj) { + struct acpi_object_notify_handler *parent_obj; + + if (handler_type & ACPI_DEVICE_NOTIFY) { + status = AE_ALREADY_EXISTS; + goto unlock_and_exit; + } + + parent_obj = ¬ify_obj->notify; + status = acpi_add_handler_object(parent_obj, + handler, + context); + goto unlock_and_exit; + } } else { /* Create a new object */ @@ -356,9 +439,10 @@ acpi_install_notify_handler(acpi_handle device, goto unlock_and_exit; } - notify_obj->notify.node = node; - notify_obj->notify.handler = handler; - notify_obj->notify.context = context; + acpi_populate_handler_object(¬ify_obj->notify, + handler_type, + handler, context, + NULL); if (handler_type & ACPI_SYSTEM_NOTIFY) { obj_desc->common_notify.system_notify = notify_obj; @@ -418,6 +502,10 @@ acpi_remove_notify_handler(acpi_handle device, goto exit; } + + /* Make sure all deferred tasks are completed */ + acpi_os_wait_events_complete(NULL); + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { goto exit; @@ -445,15 +533,6 @@ acpi_remove_notify_handler(acpi_handle device, goto unlock_and_exit; } - /* Make sure all deferred tasks are completed */ - - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - acpi_os_wait_events_complete(NULL); - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - goto exit; - } - if (handler_type & ACPI_SYSTEM_NOTIFY) { acpi_gbl_system_notify.node = NULL; acpi_gbl_system_notify.handler = NULL; @@ -488,28 +567,60 @@ acpi_remove_notify_handler(acpi_handle device, /* Object exists - make sure there's an existing handler */ if (handler_type & ACPI_SYSTEM_NOTIFY) { + struct acpi_object_notify_handler *handler_obj; + struct acpi_object_notify_handler *parent_obj; + notify_obj = obj_desc->common_notify.system_notify; if (!notify_obj) { status = AE_NOT_EXIST; goto unlock_and_exit; } - if (notify_obj->notify.handler != handler) { + handler_obj = ¬ify_obj->notify; + parent_obj = NULL; + while (handler_obj->handler != handler) { + if (handler_obj->next) { + parent_obj = handler_obj; + handler_obj = handler_obj->next; + } else { + break; + } + } + + if (handler_obj->handler != handler) { status = AE_BAD_PARAMETER; goto unlock_and_exit; } - /* Make sure all deferred tasks are completed */ - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - acpi_os_wait_events_complete(NULL); - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - goto exit; + /* + * Remove the handler. There are three possible cases. + * First, we may need to remove a non-embedded object. + * Second, we may need to remove the embedded object's + * handler data, while non-embedded objects exist. + * Finally, we may need to remove the embedded object + * entirely along with its container. + */ + if (parent_obj) { + /* Non-embedded object is being removed. */ + parent_obj->next = handler_obj->next; + ACPI_FREE(handler_obj); + } else if (notify_obj->notify.next) { + /* + * The handler matches the embedded object, but + * there are more handler objects in the list. + * Replace the embedded object's data with the + * first next object's data and remove that + * object. + */ + parent_obj = ¬ify_obj->notify; + handler_obj = notify_obj->notify.next; + *parent_obj = *handler_obj; + ACPI_FREE(handler_obj); + } else { + /* No more handler objects in the list. */ + obj_desc->common_notify.system_notify = NULL; + acpi_ut_remove_reference(notify_obj); } - - /* Remove the handler */ - obj_desc->common_notify.system_notify = NULL; - acpi_ut_remove_reference(notify_obj); } if (handler_type & ACPI_DEVICE_NOTIFY) { @@ -523,14 +634,6 @@ acpi_remove_notify_handler(acpi_handle device, status = AE_BAD_PARAMETER; goto unlock_and_exit; } - /* Make sure all deferred tasks are completed */ - - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - acpi_os_wait_events_complete(NULL); - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - goto exit; - } /* Remove the handler */ obj_desc->common_notify.device_notify = NULL; |