diff options
author | Lv Zheng <lv.zheng@intel.com> | 2014-04-04 00:37:51 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-04-20 16:59:37 -0400 |
commit | 69c841b6dd8313c9a673246cc0e2535174272cab (patch) | |
tree | 6311ddfe34636003a140c40d10d58d9687772d6c | |
parent | 06a63e33f32fd6b68cc78fd88f294d0fee6b889a (diff) |
ACPICA: Update use of acpi_os_wait_events_complete interface.
This patch cleans up all of the acpi_os_wait_events_complete() invocations to
make it to be invoked inside of ACPICA in the way to accommodate Linux's
work queue implementation.
According to the report, current Linux kernel code is facing a boot time
race issue in the acpi_remove_notify_handler(). This is because:
Linux is using work queues to implement a deferred handler call environment
while ACPICA expects OSPM to implement acpi_os_wait_events_complete() using
wait queues. The position to invoke a "waiter" is not suitable for a
"flusher" as new invocations can be scheduled after this position and
before the deletion of the handler from its management container.
Since the following commit has deleted acpi_os_wait_events_complete()
parameters, it thus might not be possible for OSPM to achieve a safe
removal using wait queues. This requires ACPICA to be changed accordingly
to "flush" handlers rather than "wait" them to be drain up:
Commit: 5ff986a2a9db11858247b71fe242fe17617229aa
Date: Wed, 16 May 2012 13:36:07 -0700
Subject: Introduce acpi_os_wait_events_complete interface.
This interface will block until asynchronous events like notifies
and GPEs are complete. Within ACPICA, it is called before a notify or GPE
handler is removed. ACPICA BZ 868.
This patch fixes this issue by invoking acpi_os_wait_events_complete() in the
way to "flush" things - it thus should be put to the position after handler
is removed from its management container but before it is destructed.
The technical concerns are:
1. MTX_NAMESPACE is used to protect things that acpi_os_wait_events_complete()
might be waiting for, thus MTX_NAMESPACE must be unlocked before
invoking acpi_os_wait_events_complete().
2. MTX_NAMESPACE is also used to implement the serialization of
acpi_install_notify_handler() and acpi_remove_notify_handler(). This patch
changes this logic, thus if there are many
acpi_install/remove_notify_handler() invoked in parallel, the
acpi_os_wait_events_complete() might face the races which could cause it
never running to an end. Normally this will require additional code to
implement a separate locking facility which is not implemented due to 3.
3. Given ACPICA users will always invoke acpi_install_notify_handler() once
during Linux module/device initialization and invoke
acpi_remove_notify_handler() once during module/device finalization,
problem stated in 2 will not happen in Linux environment due to the
mutual exclusive module/device existence, this fix thus is sufficient.
Same concerns can apply to acpi_install/remove_gpe_handler(). Reported and
tested: Ronald Vink. Fixed: Lv Zheng.
References: https://bugzilla.kernel.org/show_bug.cgi?id=60583
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Reported-and-Tested-by: Ronald Vink <ronald.vink@boskalis.com>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/acpi/acpica/evxface.c | 55 |
1 files changed, 36 insertions, 19 deletions
diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index a41a0e75b1b6..11e5803b8b41 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c | |||
@@ -239,7 +239,7 @@ acpi_remove_notify_handler(acpi_handle device, | |||
239 | union acpi_operand_object *obj_desc; | 239 | union acpi_operand_object *obj_desc; |
240 | union acpi_operand_object *handler_obj; | 240 | union acpi_operand_object *handler_obj; |
241 | union acpi_operand_object *previous_handler_obj; | 241 | union acpi_operand_object *previous_handler_obj; |
242 | acpi_status status; | 242 | acpi_status status = AE_OK; |
243 | u32 i; | 243 | u32 i; |
244 | 244 | ||
245 | ACPI_FUNCTION_TRACE(acpi_remove_notify_handler); | 245 | ACPI_FUNCTION_TRACE(acpi_remove_notify_handler); |
@@ -251,20 +251,17 @@ acpi_remove_notify_handler(acpi_handle device, | |||
251 | return_ACPI_STATUS(AE_BAD_PARAMETER); | 251 | return_ACPI_STATUS(AE_BAD_PARAMETER); |
252 | } | 252 | } |
253 | 253 | ||
254 | /* Make sure all deferred notify tasks are completed */ | ||
255 | |||
256 | acpi_os_wait_events_complete(); | ||
257 | |||
258 | status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); | ||
259 | if (ACPI_FAILURE(status)) { | ||
260 | return_ACPI_STATUS(status); | ||
261 | } | ||
262 | |||
263 | /* Root Object. Global handlers are removed here */ | 254 | /* Root Object. Global handlers are removed here */ |
264 | 255 | ||
265 | if (device == ACPI_ROOT_OBJECT) { | 256 | if (device == ACPI_ROOT_OBJECT) { |
266 | for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { | 257 | for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { |
267 | if (handler_type & (i + 1)) { | 258 | if (handler_type & (i + 1)) { |
259 | status = | ||
260 | acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); | ||
261 | if (ACPI_FAILURE(status)) { | ||
262 | return_ACPI_STATUS(status); | ||
263 | } | ||
264 | |||
268 | if (!acpi_gbl_global_notify[i].handler || | 265 | if (!acpi_gbl_global_notify[i].handler || |
269 | (acpi_gbl_global_notify[i].handler != | 266 | (acpi_gbl_global_notify[i].handler != |
270 | handler)) { | 267 | handler)) { |
@@ -277,31 +274,40 @@ acpi_remove_notify_handler(acpi_handle device, | |||
277 | 274 | ||
278 | acpi_gbl_global_notify[i].handler = NULL; | 275 | acpi_gbl_global_notify[i].handler = NULL; |
279 | acpi_gbl_global_notify[i].context = NULL; | 276 | acpi_gbl_global_notify[i].context = NULL; |
277 | |||
278 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); | ||
279 | |||
280 | /* Make sure all deferred notify tasks are completed */ | ||
281 | |||
282 | acpi_os_wait_events_complete(); | ||
280 | } | 283 | } |
281 | } | 284 | } |
282 | 285 | ||
283 | goto unlock_and_exit; | 286 | return_ACPI_STATUS(AE_OK); |
284 | } | 287 | } |
285 | 288 | ||
286 | /* All other objects: Are Notifies allowed on this object? */ | 289 | /* All other objects: Are Notifies allowed on this object? */ |
287 | 290 | ||
288 | if (!acpi_ev_is_notify_object(node)) { | 291 | if (!acpi_ev_is_notify_object(node)) { |
289 | status = AE_TYPE; | 292 | return_ACPI_STATUS(AE_TYPE); |
290 | goto unlock_and_exit; | ||
291 | } | 293 | } |
292 | 294 | ||
293 | /* Must have an existing internal object */ | 295 | /* Must have an existing internal object */ |
294 | 296 | ||
295 | obj_desc = acpi_ns_get_attached_object(node); | 297 | obj_desc = acpi_ns_get_attached_object(node); |
296 | if (!obj_desc) { | 298 | if (!obj_desc) { |
297 | status = AE_NOT_EXIST; | 299 | return_ACPI_STATUS(AE_NOT_EXIST); |
298 | goto unlock_and_exit; | ||
299 | } | 300 | } |
300 | 301 | ||
301 | /* Internal object exists. Find the handler and remove it */ | 302 | /* Internal object exists. Find the handler and remove it */ |
302 | 303 | ||
303 | for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { | 304 | for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { |
304 | if (handler_type & (i + 1)) { | 305 | if (handler_type & (i + 1)) { |
306 | status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); | ||
307 | if (ACPI_FAILURE(status)) { | ||
308 | return_ACPI_STATUS(status); | ||
309 | } | ||
310 | |||
305 | handler_obj = obj_desc->common_notify.notify_list[i]; | 311 | handler_obj = obj_desc->common_notify.notify_list[i]; |
306 | previous_handler_obj = NULL; | 312 | previous_handler_obj = NULL; |
307 | 313 | ||
@@ -329,10 +335,17 @@ acpi_remove_notify_handler(acpi_handle device, | |||
329 | handler_obj->notify.next[i]; | 335 | handler_obj->notify.next[i]; |
330 | } | 336 | } |
331 | 337 | ||
338 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); | ||
339 | |||
340 | /* Make sure all deferred notify tasks are completed */ | ||
341 | |||
342 | acpi_os_wait_events_complete(); | ||
332 | acpi_ut_remove_reference(handler_obj); | 343 | acpi_ut_remove_reference(handler_obj); |
333 | } | 344 | } |
334 | } | 345 | } |
335 | 346 | ||
347 | return_ACPI_STATUS(status); | ||
348 | |||
336 | unlock_and_exit: | 349 | unlock_and_exit: |
337 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); | 350 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
338 | return_ACPI_STATUS(status); | 351 | return_ACPI_STATUS(status); |
@@ -842,10 +855,6 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, | |||
842 | return_ACPI_STATUS(AE_BAD_PARAMETER); | 855 | return_ACPI_STATUS(AE_BAD_PARAMETER); |
843 | } | 856 | } |
844 | 857 | ||
845 | /* Make sure all deferred GPE tasks are completed */ | ||
846 | |||
847 | acpi_os_wait_events_complete(); | ||
848 | |||
849 | status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); | 858 | status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); |
850 | if (ACPI_FAILURE(status)) { | 859 | if (ACPI_FAILURE(status)) { |
851 | return_ACPI_STATUS(status); | 860 | return_ACPI_STATUS(status); |
@@ -897,9 +906,17 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, | |||
897 | (void)acpi_ev_add_gpe_reference(gpe_event_info); | 906 | (void)acpi_ev_add_gpe_reference(gpe_event_info); |
898 | } | 907 | } |
899 | 908 | ||
909 | acpi_os_release_lock(acpi_gbl_gpe_lock, flags); | ||
910 | (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); | ||
911 | |||
912 | /* Make sure all deferred GPE tasks are completed */ | ||
913 | |||
914 | acpi_os_wait_events_complete(); | ||
915 | |||
900 | /* Now we can free the handler object */ | 916 | /* Now we can free the handler object */ |
901 | 917 | ||
902 | ACPI_FREE(handler); | 918 | ACPI_FREE(handler); |
919 | return_ACPI_STATUS(status); | ||
903 | 920 | ||
904 | unlock_and_exit: | 921 | unlock_and_exit: |
905 | acpi_os_release_lock(acpi_gbl_gpe_lock, flags); | 922 | acpi_os_release_lock(acpi_gbl_gpe_lock, flags); |