* [RFC PATCH 1/3] ACPICA: Utilities: Add deferred lock free contexts for deleting operand objects
2015-09-11 2:05 [RFC PATCH 0/3] ACPICA: Events: Fix notify handler atomicity Lv Zheng
@ 2015-09-11 2:05 ` Lv Zheng
2015-09-11 2:05 ` [RFC PATCH 2/3] ACPI / osl: Add OSL_GC_HANDLER/OSL_GC_THREAD support Lv Zheng
2015-09-11 2:05 ` [RFC PATCH 3/3] ACPICA: Events: Fix reference counting code for per-object notify handlers Lv Zheng
2 siblings, 0 replies; 4+ messages in thread
From: Lv Zheng @ 2015-09-11 2:05 UTC (permalink / raw)
To: Rafael J. Wysocki, Len Brown
Cc: Lv Zheng, Lv Zheng, linux-acpi, Lee, Chun-Yi, Hanjun Guo
When acpi_ut_delete_internal_obj() is invoked, reference count should be zero
which means this context is the only context remained referencing this
object. Normally, we may just unlock namespace mutex (ACPI_MTX_NAMESPACE)
before invoking this function, but since this function is invoked from
different environment, doing this have side effects:
1. Other ACPI internal mutexes may be held orderly when this function is
invoked. Without unlocking such locks, we still cannot achieve a lock
free environment.
2. For some environment, it might not be safe to unlock the namespace
mutex right in that context.
So we introduces a deferred unlocked environment for further cleanups.
There are two choices to convert this function into a lock free style:
1. Deferred work queue: this may not be performance friendly as in order to
__delete__ the object, we __create__ a new OS object (work queue item)
to achieve each deletion, which may lead to a failure when the memory
resource is heavily used.
2. Garbage collection: the deleted objects are linked together and at each
exit of such a deletion, a garbage cleaner function is invoked where
this function is invoked in a lock free style.
This patch implements both choices:
1. OSL_GC_HANDLER: This is a deferred work item, it is used in case there
link point available to link the object into the garbage;
2. OSL_GC_THREAD: The linked deleted objects are destructed in a thread.
Note that in the slow path, we still need to __create__ a new OS object in
order to __delete__ the operand object. Lv Zheng.
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Cc: Lee, Chun-Yi <jlee@suse.com>
Cc: Hanjun Guo <hanjun.guo@linaro.org>
---
drivers/acpi/acpica/acglobal.h | 5 ++
drivers/acpi/acpica/utdelete.c | 155 +++++++++++++++++++++++++++++++++++++---
include/acpi/acpiosxf.h | 2 +
3 files changed, 153 insertions(+), 9 deletions(-)
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index 09f37b5..e7f0f82 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -161,6 +161,11 @@ ACPI_GLOBAL(u32, acpi_gbl_owner_id_mask[ACPI_NUM_OWNERID_MASKS]);
ACPI_GLOBAL(u8, acpi_gbl_last_owner_id_index);
ACPI_GLOBAL(u8, acpi_gbl_next_owner_id_offset);
+/* Garbage collection support */
+
+ACPI_INIT_GLOBAL(union acpi_operand_object *, acpi_gbl_garbage_operands, NULL);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_gc_thread_created, FALSE);
+
/* Initialization sequencing */
ACPI_GLOBAL(u8, acpi_gbl_reg_methods_executed);
diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c
index 1638312..ea737f4 100644
--- a/drivers/acpi/acpica/utdelete.c
+++ b/drivers/acpi/acpica/utdelete.c
@@ -51,26 +51,46 @@
ACPI_MODULE_NAME("utdelete")
/* Local prototypes */
-static void acpi_ut_delete_internal_obj(union acpi_operand_object *object);
+static void ACPI_SYSTEM_XFACE acpi_ut_delete_object_deferred(void *context);
+
+static void ACPI_SYSTEM_XFACE acpi_ut_delete_object_thread(void *unused);
static void
acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action);
/*******************************************************************************
*
- * FUNCTION: acpi_ut_delete_internal_obj
+ * FUNCTION: acpi_ut_delete_object_deferred
*
- * PARAMETERS: object - Object to be deleted
+ * PARAMETERS: context - Execution info
*
* RETURN: None
*
* DESCRIPTION: Low level object deletion, after reference counts have been
- * updated (All reference counts, including sub-objects!)
+ * updated to zero.
+ * There are two design requirements for the deletion function:
+ * 1. Since multiple wait/lock code may be invoked for the
+ * destruction of the object, the deletion function should be
+ * implemented in a lock free style so that individual deletion
+ * code piece can wait/lock using different wait/lock semantics.
+ * Since this context here is the only context that is still
+ * referencing the object, we needn't worry about the race
+ * conditions occurred without locking the whole function
+ * around.
+ * 2. Since operand object may contain many sub-objects typed as
+ * operand objects, in order not to increase stack consumption
+ * beyond software control, we need to defer executing each
+ * deletion of each operand object.
+ * Using acpi_os_execute() may not be a performance friendly choice, but it is
+ * the simplest choice for now to meet the above requirements.
*
******************************************************************************/
-static void acpi_ut_delete_internal_obj(union acpi_operand_object *object)
+static void ACPI_SYSTEM_XFACE acpi_ut_delete_object_deferred(void *context)
{
+ acpi_status status;
+ union acpi_operand_object *object =
+ ACPI_CAST_PTR(union acpi_operand_object, context);
void *obj_pointer = NULL;
union acpi_operand_object *handler_desc;
union acpi_operand_object *second_desc;
@@ -78,13 +98,22 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object)
union acpi_operand_object *start_desc;
union acpi_operand_object **last_obj_ptr;
- ACPI_FUNCTION_TRACE_PTR(ut_delete_internal_obj, object);
+ ACPI_FUNCTION_TRACE_PTR(ut_delete_object_deferred, object);
if (!object) {
return_VOID;
}
/*
+ * Keep old logic where the namespace mutex is always locked for an
+ * operand object deletion in order to be regression safe.
+ */
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_VOID;
+ }
+
+ /*
* Must delete or free any pointers within the object that are not
* actual ACPI objects (for example, a raw buffer pointer).
*/
@@ -335,11 +364,61 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object)
object, acpi_ut_get_object_type_name(object)));
acpi_ut_delete_object_desc(object);
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return_VOID;
}
/*******************************************************************************
*
+ * FUNCTION: acpi_ut_delete_object_thread
+ *
+ * PARAMETERS: unused - Execution info (unused)
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: This thread iterates garbage operand list and destroy operands
+ * linked in the list.
+ *
+ ******************************************************************************/
+
+static void ACPI_SYSTEM_XFACE acpi_ut_delete_object_thread(void *context)
+{
+ acpi_cpu_flags lock_flags;
+ union acpi_operand_object *gc_obj;
+
+ ACPI_FUNCTION_ENTRY();
+
+ lock_flags = acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
+ while (acpi_gbl_gc_thread_created) {
+ while (acpi_gbl_garbage_operands) {
+ gc_obj =
+ ACPI_CAST_PTR(union acpi_operand_object,
+ acpi_gbl_garbage_operands);
+ acpi_gbl_garbage_operands = gc_obj->common.next_object;
+ gc_obj->common.next_object = NULL;
+
+ /* Delete the object in a lock free environment */
+
+ acpi_os_release_lock(acpi_gbl_reference_count_lock,
+ lock_flags);
+ acpi_ut_delete_object_deferred(gc_obj);
+ lock_flags =
+ acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
+ }
+
+ /* Sleep until more garbage operands are detected */
+
+ acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
+ acpi_os_sleep(50);
+ lock_flags =
+ acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
+ }
+ acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
+ return;
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_ut_delete_internal_object_list
*
* PARAMETERS: obj_list - Pointer to the list to be deleted
@@ -388,6 +467,7 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
u16 original_count;
u16 new_count = 0;
acpi_cpu_flags lock_flags;
+ acpi_status status;
ACPI_FUNCTION_NAME(ut_update_ref_count);
@@ -445,10 +525,67 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
"Obj %p Type %.2X Refs %.2X [Decremented]\n",
object, object->common.type, new_count));
- /* Actually delete the object on a reference count of zero */
-
+ /*
+ * Actually delete the object on a reference count of zero. As some
+ * synchronizations may be required for destructing sub-objects,
+ * defer executing the deletion to a lock free environment.
+ */
if (new_count == 0) {
- acpi_ut_delete_internal_obj(object);
+ if (object->common.next_object) {
+
+ /* Slow path: create a work queue to free the object */
+
+ status = acpi_os_execute(OSL_GC_HANDLER,
+ acpi_ut_delete_object_deferred,
+ object);
+ if (ACPI_FAILURE(status)) {
+ acpi_ut_delete_object_deferred(object);
+ }
+ } else {
+ /* Acquire reference count lock for GC variables */
+
+ lock_flags =
+ acpi_os_acquire_lock
+ (acpi_gbl_reference_count_lock);
+
+ /* Fast path: link the object into a garbage list */
+
+ object->common.next_object =
+ acpi_gbl_garbage_operands;
+ acpi_gbl_garbage_operands = object;
+
+ /*
+ * Synchronize here to create the garbage collection thread
+ * when memory is not low.
+ */
+ if (!acpi_gbl_gc_thread_created) {
+ acpi_gbl_gc_thread_created = TRUE;
+ acpi_os_release_lock
+ (acpi_gbl_reference_count_lock,
+ lock_flags);
+
+ /*
+ * Some hosts require this to be done in a non atomic
+ * environment.
+ */
+ status = acpi_os_execute(OSL_GC_THREAD,
+ acpi_ut_delete_object_thread,
+ NULL);
+
+ lock_flags =
+ acpi_os_acquire_lock
+ (acpi_gbl_reference_count_lock);
+ if (ACPI_FAILURE(status)) {
+ acpi_gbl_gc_thread_created =
+ FALSE;
+ }
+ }
+
+ /* Release reference count lock for GC variables */
+
+ acpi_os_release_lock
+ (acpi_gbl_reference_count_lock, lock_flags);
+ }
}
break;
diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h
index a54ad1c..6a8f6c3 100644
--- a/include/acpi/acpiosxf.h
+++ b/include/acpi/acpiosxf.h
@@ -55,6 +55,8 @@ typedef enum {
OSL_GLOBAL_LOCK_HANDLER,
OSL_NOTIFY_HANDLER,
OSL_GPE_HANDLER,
+ OSL_GC_THREAD,
+ OSL_GC_HANDLER,
OSL_DEBUGGER_THREAD,
OSL_EC_POLL_HANDLER,
OSL_EC_BURST_HANDLER
--
1.7.10
^ permalink raw reply related [flat|nested] 4+ messages in thread* [RFC PATCH 2/3] ACPI / osl: Add OSL_GC_HANDLER/OSL_GC_THREAD support
2015-09-11 2:05 [RFC PATCH 0/3] ACPICA: Events: Fix notify handler atomicity Lv Zheng
2015-09-11 2:05 ` [RFC PATCH 1/3] ACPICA: Utilities: Add deferred lock free contexts for deleting operand objects Lv Zheng
@ 2015-09-11 2:05 ` Lv Zheng
2015-09-11 2:05 ` [RFC PATCH 3/3] ACPICA: Events: Fix reference counting code for per-object notify handlers Lv Zheng
2 siblings, 0 replies; 4+ messages in thread
From: Lv Zheng @ 2015-09-11 2:05 UTC (permalink / raw)
To: Rafael J. Wysocki, Len Brown
Cc: Lv Zheng, Lv Zheng, linux-acpi, Lee, Chun-Yi, Hanjun Guo
This patch enables Linux capability of supporting ACPICA operand object GC.
Note that, when the operand object is deferred freed in the early stage,
the GC work queue and the GC thread cannot be used for the execution,
acpi_os_initialized can be used to indicate such periods and the operand
object can either be freed in ACPICA non deferred path (work queue) or
linked together for later try.
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Cc: Lee, Chun-Yi <jlee@suse.com>
Cc: Hanjun Guo <hanjun.guo@linaro.org>
---
drivers/acpi/bus.c | 4 ++--
drivers/acpi/osl.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 50 insertions(+), 6 deletions(-)
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 46506e7..7455f69 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -982,8 +982,6 @@ static int __init acpi_bus_init(void)
int result;
acpi_status status;
- acpi_os_initialize1();
-
status = acpi_enable_subsystem(ACPI_NO_ACPI_ENABLE);
if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX
@@ -1085,6 +1083,8 @@ static int __init acpi_init(void)
acpi_kobj = NULL;
}
+ acpi_os_initialize1();
+
init_acpi_device_notify();
result = acpi_bus_init();
if (result) {
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 739a4a6..3fb16a7 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -40,6 +40,7 @@
#include <linux/list.h>
#include <linux/jiffies.h>
#include <linux/semaphore.h>
+#include <linux/kthread.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -79,8 +80,10 @@ static acpi_osd_handler acpi_irq_handler;
static void *acpi_irq_context;
static struct workqueue_struct *kacpid_wq;
static struct workqueue_struct *kacpi_notify_wq;
+static struct workqueue_struct *kacpi_gc_wq;
static struct workqueue_struct *kacpi_hotplug_wq;
static bool acpi_os_initialized;
+static struct acpi_os_dpc acpi_gc_thread;
/*
* This list of permanent mappings is for memory that may be accessed from
@@ -1100,6 +1103,16 @@ static void acpi_os_execute_deferred(struct work_struct *work)
kfree(dpc);
}
+static int acpi_os_threaded_dpc(void *context)
+{
+ struct acpi_os_dpc *dpc = (struct acpi_os_dpc *)context;
+
+ pr_info("ACPI: GC thread is online.\n");
+ dpc->function(dpc->context);
+ pr_info("ACPI: GC thread is offline.\n");
+ return 0;
+}
+
/*******************************************************************************
*
* FUNCTION: acpi_os_execute
@@ -1122,10 +1135,27 @@ acpi_status acpi_os_execute(acpi_execute_type type,
struct acpi_os_dpc *dpc;
struct workqueue_struct *queue;
int ret;
+ struct task_struct *t;
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
"Scheduling function [%p(%p)] for deferred execution.\n",
function, context));
+ if (type == OSL_GC_THREAD) {
+ if (acpi_os_initialized) {
+ acpi_gc_thread.function = function;
+ acpi_gc_thread.context = context;
+ t = kthread_create(acpi_os_threaded_dpc,
+ (void *)&acpi_gc_thread, "kacpi_gc_thread");
+ if (IS_ERR(t)) {
+ pr_err("Failed to create GC thread.\n");
+ status = AE_ERROR;
+ } else
+ wake_up_process(t);
+ } else
+ status = AE_NO_MEMORY;
+ goto out_thread;
+ }
+
/*
* Allocate/initialize DPC structure. Note that this memory will be
* freed by the callee. The kernel handles the work_struct list in a
@@ -1150,11 +1180,20 @@ acpi_status acpi_os_execute(acpi_execute_type type,
if (type == OSL_NOTIFY_HANDLER) {
queue = kacpi_notify_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
- } else {
+ } else if (type == OSL_GPE_HANDLER) {
queue = kacpid_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
+ } else if (type == OSL_GC_HANDLER) {
+ queue = kacpi_gc_wq;
+ INIT_WORK(&dpc->work, acpi_os_execute_deferred);
+ } else {
+ pr_err("Unsupported os_execute type %d.\n", type);
+ status = AE_ERROR;
}
+ if (ACPI_FAILURE(status))
+ goto err_workqueue;
+
/*
* On some machines, a software-initiated SMI causes corruption unless
* the SMI runs on CPU 0. An SMI can be initiated by any AML, but
@@ -1163,13 +1202,15 @@ acpi_status acpi_os_execute(acpi_execute_type type,
* queueing on CPU 0.
*/
ret = queue_work_on(0, queue, &dpc->work);
-
if (!ret) {
printk(KERN_ERR PREFIX
"Call to queue_work() failed.\n");
status = AE_ERROR;
- kfree(dpc);
}
+err_workqueue:
+ if (ACPI_FAILURE(status))
+ kfree(dpc);
+out_thread:
return status;
}
EXPORT_SYMBOL(acpi_os_execute);
@@ -1839,18 +1880,20 @@ acpi_status __init acpi_os_initialize(void)
rv = acpi_os_map_generic_address(&acpi_gbl_FADT.reset_register);
pr_debug(PREFIX "%s: map reset_reg status %d\n", __func__, rv);
}
- acpi_os_initialized = true;
return AE_OK;
}
acpi_status __init acpi_os_initialize1(void)
{
+ acpi_os_initialized = true;
kacpid_wq = alloc_workqueue("kacpid", 0, 1);
kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1);
+ kacpi_gc_wq = alloc_workqueue("kacpi_gc_handler", 0, 1);
kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0);
BUG_ON(!kacpid_wq);
BUG_ON(!kacpi_notify_wq);
+ BUG_ON(!kacpi_gc_wq);
BUG_ON(!kacpi_hotplug_wq);
acpi_install_interface_handler(acpi_osi_handler);
acpi_osi_setup_late();
@@ -1873,6 +1916,7 @@ acpi_status acpi_os_terminate(void)
destroy_workqueue(kacpid_wq);
destroy_workqueue(kacpi_notify_wq);
+ destroy_workqueue(kacpi_gc_wq);
destroy_workqueue(kacpi_hotplug_wq);
return AE_OK;
--
1.7.10
^ permalink raw reply related [flat|nested] 4+ messages in thread* [RFC PATCH 3/3] ACPICA: Events: Fix reference counting code for per-object notify handlers
2015-09-11 2:05 [RFC PATCH 0/3] ACPICA: Events: Fix notify handler atomicity Lv Zheng
2015-09-11 2:05 ` [RFC PATCH 1/3] ACPICA: Utilities: Add deferred lock free contexts for deleting operand objects Lv Zheng
2015-09-11 2:05 ` [RFC PATCH 2/3] ACPI / osl: Add OSL_GC_HANDLER/OSL_GC_THREAD support Lv Zheng
@ 2015-09-11 2:05 ` Lv Zheng
2 siblings, 0 replies; 4+ messages in thread
From: Lv Zheng @ 2015-09-11 2:05 UTC (permalink / raw)
To: Rafael J. Wysocki, Len Brown
Cc: Lv Zheng, Lv Zheng, linux-acpi, Lee, Chun-Yi, Hanjun Guo
The reference counting code of Notify handler object is abnormal. It
doesn't follow the coding rule that the reference increment should/must
only happen to a pointer reference and the reference decrement should /must
only happen to a pointer dereference.
This patch fixes per-object notify handler reference counting. The global
notify handlers are not covered by this patch. Lv Zheng.
Reference: https://bugs.acpica.org/show_bug.cgi?id=1115
Reported-by: Lee, Chun-Yi <jlee@suse.com>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Cc: Lee, Chun-Yi <jlee@suse.com>
Cc: Hanjun Guo <hanjun.guo@linaro.org>
---
drivers/acpi/acpica/acevents.h | 4 ++
drivers/acpi/acpica/acobject.h | 2 +-
drivers/acpi/acpica/evmisc.c | 134 +++++++++++++++++++++++++++++++++++++++-
drivers/acpi/acpica/evxface.c | 66 +++-----------------
drivers/acpi/acpica/utdelete.c | 36 +++++------
drivers/acpi/acpica/utstate.c | 22 ++++++-
6 files changed, 178 insertions(+), 86 deletions(-)
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
index 228704b7..dc0b19e 100644
--- a/drivers/acpi/acpica/acevents.h
+++ b/drivers/acpi/acpica/acevents.h
@@ -64,6 +64,10 @@ acpi_status
acpi_ev_queue_notify_request(struct acpi_namespace_node *node,
u32 notify_value);
+acpi_status
+acpi_ev_delete_notify_handlers(union acpi_operand_object *object,
+ u32 handler_type, acpi_notify_handler handler);
+
/*
* evglock - Global Lock support
*/
diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
index 0bd02c4..35625ec 100644
--- a/drivers/acpi/acpica/acobject.h
+++ b/drivers/acpi/acpica/acobject.h
@@ -207,7 +207,7 @@ struct acpi_object_method {
* Common fields for objects that support ASL notifications
*/
#define ACPI_COMMON_NOTIFY_INFO \
- union acpi_operand_object *notify_list[2]; /* Handlers for system/device notifies */\
+ union acpi_operand_object *notify_list[ACPI_NUM_NOTIFY_TYPES]; /* Handlers for system/device notifies */\
union acpi_operand_object *handler; /* Handler for Address space */
struct acpi_object_notify_common { /* COMMON NOTIFY for POWER, PROCESSOR, DEVICE, and THERMAL */
diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c
index f7c9dfe..10ab07c 100644
--- a/drivers/acpi/acpica/evmisc.c
+++ b/drivers/acpi/acpica/evmisc.c
@@ -161,6 +161,7 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node,
info->notify.value = (u16)notify_value;
info->notify.handler_list_id = handler_list_id;
info->notify.handler_list_head = handler_list_head;
+ acpi_ut_add_reference(handler_list_head);
info->notify.global = &acpi_gbl_global_notify[handler_list_id];
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
@@ -196,6 +197,8 @@ static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context)
{
union acpi_generic_state *info = (union acpi_generic_state *)context;
union acpi_operand_object *handler_obj;
+ union acpi_operand_object *next_obj;
+ acpi_status status;
ACPI_FUNCTION_ENTRY();
@@ -209,21 +212,148 @@ static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context)
/* Now invoke the local notify handler(s) if any are installed */
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+
handler_obj = info->notify.handler_list_head;
+ acpi_ut_add_reference(handler_obj);
while (handler_obj) {
+ next_obj =
+ handler_obj->notify.next[info->notify.handler_list_id];
+ acpi_ut_add_reference(next_obj);
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
handler_obj->notify.handler(info->notify.node,
info->notify.value,
handler_obj->notify.context);
+ acpi_ut_remove_reference(handler_obj);
- handler_obj =
- handler_obj->notify.next[info->notify.handler_list_id];
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return;
+ }
+ handler_obj = next_obj;
}
/* All done with the info object */
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+error_exit:
acpi_ut_delete_generic_state(info);
}
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_delete_notify_handlers
+ *
+ * PARAMETERS: object - The object for which notifies will be handled
+ * handler_type - The type of handler:
+ * ACPI_SYSTEM_NOTIFY: System Handler (00-7F)
+ * ACPI_DEVICE_NOTIFY: Device Handler (80-FF)
+ * ACPI_ALL_NOTIFY: Both System and Device
+ * handler - Address of the handler
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove matched handlers for notifications on the operand object
+ * which should have just been detached from an ACPI Device,
+ * thermal_zone, or Processor namespace node.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_delete_notify_handlers(union acpi_operand_object *object,
+ u32 handler_type, acpi_notify_handler handler)
+{
+ union acpi_operand_object *handler_obj;
+ union acpi_operand_object *previous_handler_obj;
+ acpi_status status = AE_OK;
+ u32 i;
+ union acpi_operand_object *dead_handler_list = NULL;
+ union acpi_operand_object *dead_handler_obj = NULL;
+ u8 dead_handler_duplicated;
+
+ ACPI_FUNCTION_TRACE(ev_delete_notify_handlers);
+
+ /* Internal object exists. Find the handler and remove it */
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) {
+ if (handler_type & (i + 1)) {
+ handler_obj = object->common_notify.notify_list[i];
+ previous_handler_obj = NULL;
+
+ /* Attempt to find the handler in the handler list */
+
+ if (handler) {
+ while (handler_obj &&
+ (handler_obj->notify.handler !=
+ handler)) {
+ previous_handler_obj = handler_obj;
+ handler_obj =
+ handler_obj->notify.next[i];
+ }
+ }
+
+ if (!handler_obj) {
+ continue;
+ }
+
+ /* Remove the handler object from the list */
+
+ if (previous_handler_obj) { /* Handler is not at the list head */
+ previous_handler_obj->notify.next[i] =
+ handler_obj->notify.next[i];
+ } else { /* Handler is at the list head */
+
+ object->common_notify.notify_list[i] =
+ handler_obj->notify.next[i];
+ }
+ acpi_ut_remove_reference(handler_obj);
+
+ /* Store removed handler object if it is not duplicated */
+
+ dead_handler_duplicated = FALSE;
+ dead_handler_obj = dead_handler_list;
+ while (dead_handler_obj) {
+ if (dead_handler_obj == handler_obj) {
+ dead_handler_duplicated = TRUE;
+ break;
+ }
+ dead_handler_obj =
+ dead_handler_obj->notify.next[0];
+ }
+ if (!dead_handler_duplicated) {
+ handler_obj->notify.next[0] = dead_handler_list;
+ dead_handler_list = handler_obj;
+ }
+ }
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+ /* Make sure all deferred notify tasks are completed */
+
+ acpi_os_wait_events_complete();
+
+ /* Destroy removed handler objects */
+
+ handler_obj = dead_handler_list;
+ while (handler_obj) {
+ dead_handler_list = handler_obj->notify.next[0];
+ acpi_ut_remove_reference(handler_obj);
+ handler_obj = dead_handler_list;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
#if (!ACPI_REDUCED_HARDWARE)
/******************************************************************************
*
diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c
index 81f2d9e..34a78b0 100644
--- a/drivers/acpi/acpica/evxface.c
+++ b/drivers/acpi/acpica/evxface.c
@@ -208,16 +208,11 @@ acpi_install_notify_handler(acpi_handle device,
handler_obj->notify.next[i] =
obj_desc->common_notify.notify_list[i];
+ acpi_ut_add_reference(handler_obj);
obj_desc->common_notify.notify_list[i] = handler_obj;
}
}
- /* Add an extra reference if handler was installed in both lists */
-
- if (handler_type == ACPI_ALL_NOTIFY) {
- acpi_ut_add_reference(handler_obj);
- }
-
unlock_and_exit:
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return_ACPI_STATUS(status);
@@ -248,8 +243,6 @@ acpi_remove_notify_handler(acpi_handle device,
struct acpi_namespace_node *node =
ACPI_CAST_PTR(struct acpi_namespace_node, device);
union acpi_operand_object *obj_desc;
- union acpi_operand_object *handler_obj;
- union acpi_operand_object *previous_handler_obj;
acpi_status status = AE_OK;
u32 i;
@@ -276,8 +269,10 @@ acpi_remove_notify_handler(acpi_handle device,
if (!acpi_gbl_global_notify[i].handler ||
(acpi_gbl_global_notify[i].handler !=
handler)) {
- status = AE_NOT_EXIST;
- goto unlock_and_exit;
+ (void)
+ acpi_ut_release_mutex
+ (ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
@@ -310,55 +305,8 @@ acpi_remove_notify_handler(acpi_handle device,
return_ACPI_STATUS(AE_NOT_EXIST);
}
- /* Internal object exists. Find the handler and remove it */
-
- for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) {
- if (handler_type & (i + 1)) {
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- handler_obj = obj_desc->common_notify.notify_list[i];
- previous_handler_obj = NULL;
-
- /* Attempt to find the handler in the handler list */
-
- while (handler_obj &&
- (handler_obj->notify.handler != handler)) {
- previous_handler_obj = handler_obj;
- handler_obj = handler_obj->notify.next[i];
- }
-
- if (!handler_obj) {
- status = AE_NOT_EXIST;
- goto unlock_and_exit;
- }
-
- /* Remove the handler object from the list */
-
- if (previous_handler_obj) { /* Handler is not at the list head */
- previous_handler_obj->notify.next[i] =
- handler_obj->notify.next[i];
- } else { /* Handler is at the list head */
-
- obj_desc->common_notify.notify_list[i] =
- handler_obj->notify.next[i];
- }
-
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
-
- /* Make sure all deferred notify tasks are completed */
-
- acpi_os_wait_events_complete();
- acpi_ut_remove_reference(handler_obj);
- }
- }
-
- return_ACPI_STATUS(status);
-
-unlock_and_exit:
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ status =
+ acpi_ev_delete_notify_handlers(obj_desc, handler_type, handler);
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c
index ea737f4..619ab54 100644
--- a/drivers/acpi/acpica/utdelete.c
+++ b/drivers/acpi/acpica/utdelete.c
@@ -190,6 +190,20 @@ static void ACPI_SYSTEM_XFACE acpi_ut_delete_object_deferred(void *context)
acpi_ut_remove_reference(handler_desc);
handler_desc = next_desc;
}
+
+ /*lint -fallthrough */
+
+ case ACPI_TYPE_POWER:
+ /*
+ * Update the notify objects for these types (if present)
+ * Two lists, system and device notify handlers.
+ */
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ acpi_ev_delete_notify_handlers(object, ACPI_ALL_NOTIFY, NULL);
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_VOID;
+ }
break;
case ACPI_TYPE_MUTEX:
@@ -635,7 +649,6 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action)
acpi_status status = AE_OK;
union acpi_generic_state *state_list = NULL;
union acpi_operand_object *next_object = NULL;
- union acpi_operand_object *prev_object;
union acpi_generic_state *state;
u32 i;
@@ -656,27 +669,6 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action)
* Different object types have different subobjects.
*/
switch (object->common.type) {
- case ACPI_TYPE_DEVICE:
- case ACPI_TYPE_PROCESSOR:
- case ACPI_TYPE_POWER:
- case ACPI_TYPE_THERMAL:
- /*
- * Update the notify objects for these types (if present)
- * Two lists, system and device notify handlers.
- */
- for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) {
- prev_object =
- object->common_notify.notify_list[i];
- while (prev_object) {
- next_object =
- prev_object->notify.next[i];
- acpi_ut_update_ref_count(prev_object,
- action);
- prev_object = next_object;
- }
- }
- break;
-
case ACPI_TYPE_PACKAGE:
/*
* We must update all the sub-objects of the package,
diff --git a/drivers/acpi/acpica/utstate.c b/drivers/acpi/acpica/utstate.c
index f201171..a959b3f 100644
--- a/drivers/acpi/acpica/utstate.c
+++ b/drivers/acpi/acpica/utstate.c
@@ -297,12 +297,30 @@ union acpi_generic_state *acpi_ut_create_control_state(void)
void acpi_ut_delete_generic_state(union acpi_generic_state *state)
{
+ union acpi_operand_object *handler_list_head;
+
ACPI_FUNCTION_ENTRY();
/* Ignore null state */
- if (state) {
- (void)acpi_os_release_object(acpi_gbl_state_cache, state);
+ if (!state) {
+ return;
+ }
+
+ switch (state->common.descriptor_type) {
+ case ACPI_DESC_TYPE_STATE_NOTIFY:
+
+ if (state->notify.handler_list_head) {
+ handler_list_head = state->notify.handler_list_head;
+ state->notify.handler_list_head = NULL;
+ acpi_ut_remove_reference(handler_list_head);
+ }
+ break;
+
+ default:
+
+ break;
}
+ (void)acpi_os_release_object(acpi_gbl_state_cache, state);
return;
}
--
1.7.10
^ permalink raw reply related [flat|nested] 4+ messages in thread