| From 2d8a30bf6dccc78541f1bde0c3db56c457f95545 Mon Sep 17 00:00:00 2001 |
| From: Li Zhong <zhong@linux.vnet.ibm.com> |
| Date: Tue, 24 Jul 2012 15:02:49 -0700 |
| Subject: [PATCH] Fix a dead loop in async_synchronize_full() |
| |
| [commit 45516ddc16abc923104d78bb3eb772ac0a09e33e in v3.0.44 - paulg ] |
| |
| [Fixed upstream by commits 2955b47d2c1983998a8c5915cb96884e67f7cb53 and |
| a4683487f90bfe3049686fc5c566bdc1ad03ace6 from Dan Williams, but they are much |
| more intrusive than this tiny fix, according to Andrew - gregkh] |
| |
| This patch tries to fix a dead loop in async_synchronize_full(), which |
| could be seen when preemption is disabled on a single cpu machine. |
| |
| void async_synchronize_full(void) |
| { |
| do { |
| async_synchronize_cookie(next_cookie); |
| } while (!list_empty(&async_running) || ! |
| list_empty(&async_pending)); |
| } |
| |
| async_synchronize_cookie() calls async_synchronize_cookie_domain() with |
| &async_running as the default domain to synchronize. |
| |
| However, there might be some works in the async_pending list from other |
| domains. On a single cpu system, without preemption, there is no chance |
| for the other works to finish, so async_synchronize_full() enters a dead |
| loop. |
| |
| It seems async_synchronize_full() wants to synchronize all entries in |
| all running lists(domains), so maybe we could just check the entry_count |
| to know whether all works are finished. |
| |
| Currently, async_synchronize_cookie_domain() expects a non-NULL running |
| list ( if NULL, there would be NULL pointer dereference ), so maybe a |
| NULL pointer could be used as an indication for the functions to |
| synchronize all works in all domains. |
| |
| Reported-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> |
| Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com> |
| Tested-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> |
| Tested-by: Christian Kujau <lists@nerdbynature.de> |
| Cc: Andrew Morton <akpm@linux-foundation.org> |
| Cc: Dan Williams <dan.j.williams@gmail.com> |
| Cc: Christian Kujau <lists@nerdbynature.de> |
| Cc: Andrew Morton <akpm@linux-foundation.org> |
| Cc: Cong Wang <xiyou.wangcong@gmail.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| --- |
| kernel/async.c | 13 +++++++++---- |
| 1 file changed, 9 insertions(+), 4 deletions(-) |
| |
| diff --git a/kernel/async.c b/kernel/async.c |
| index 15319d6c18fe..b640ffc3f5a1 100644 |
| --- a/kernel/async.c |
| +++ b/kernel/async.c |
| @@ -94,6 +94,13 @@ static async_cookie_t __lowest_in_progress(struct list_head *running) |
| { |
| struct async_entry *entry; |
| |
| + if (!running) { /* just check the entry count */ |
| + if (atomic_read(&entry_count)) |
| + return 0; /* smaller than any cookie */ |
| + else |
| + return next_cookie; |
| + } |
| + |
| if (!list_empty(running)) { |
| entry = list_first_entry(running, |
| struct async_entry, list); |
| @@ -249,9 +256,7 @@ EXPORT_SYMBOL_GPL(async_schedule_domain); |
| */ |
| void async_synchronize_full(void) |
| { |
| - do { |
| - async_synchronize_cookie(next_cookie); |
| - } while (!list_empty(&async_running) || !list_empty(&async_pending)); |
| + async_synchronize_cookie_domain(next_cookie, NULL); |
| } |
| EXPORT_SYMBOL_GPL(async_synchronize_full); |
| |
| @@ -271,7 +276,7 @@ EXPORT_SYMBOL_GPL(async_synchronize_full_domain); |
| /** |
| * async_synchronize_cookie_domain - synchronize asynchronous function calls within a certain domain with cookie checkpointing |
| * @cookie: async_cookie_t to use as checkpoint |
| - * @running: running list to synchronize on |
| + * @running: running list to synchronize on, NULL indicates all lists |
| * |
| * This function waits until all asynchronous function calls for the |
| * synchronization domain specified by the running list @list submitted |
| -- |
| 1.8.5.2 |
| |