linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] workqueue: move WORKER_REBIND clearing in rebind_workers() to the end of the function
@ 2012-09-05  6:12 Tejun Heo
  2012-09-05  6:16 ` [PATCH 2/2] workqueue: fix possible deadlock in idle worker rebinding Tejun Heo
  0 siblings, 1 reply; 4+ messages in thread
From: Tejun Heo @ 2012-09-05  6:12 UTC (permalink / raw)
  To: Lai Jiangshan, linux-kernel

This doesn't make any functional difference and is purely to help the
next patch to be simpler.

Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Lai Jiangshan <laijs@cn.fujitsu.com>
---
Here are the split patches.  Will push these through wq/for-3.6-fixes.

Thanks.

 kernel/workqueue.c |   26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -1422,19 +1422,7 @@ retry:
 		goto retry;
 	}
 
-	/*
-	 * All idle workers are rebound and waiting for %WORKER_REBIND to
-	 * be cleared inside idle_worker_rebind().  Clear and release.
-	 * Clearing %WORKER_REBIND from this foreign context is safe
-	 * because these workers are still guaranteed to be idle.
-	 */
-	for_each_worker_pool(pool, gcwq)
-		list_for_each_entry(worker, &pool->idle_list, entry)
-			worker->flags &= ~WORKER_REBIND;
-
-	wake_up_all(&gcwq->rebind_hold);
-
-	/* rebind busy workers */
+	/* all idle workers are rebound, rebind busy workers */
 	for_each_busy_worker(worker, i, pos, gcwq) {
 		struct work_struct *rebind_work = &worker->rebind_work;
 		unsigned long worker_flags = worker->flags;
@@ -1454,6 +1442,18 @@ retry:
 			    worker->scheduled.next,
 			    work_color_to_flags(WORK_NO_COLOR));
 	}
+
+	/*
+	 * All idle workers are rebound and waiting for %WORKER_REBIND to
+	 * be cleared inside idle_worker_rebind().  Clear and release.
+	 * Clearing %WORKER_REBIND from this foreign context is safe
+	 * because these workers are still guaranteed to be idle.
+	 */
+	for_each_worker_pool(pool, gcwq)
+		list_for_each_entry(worker, &pool->idle_list, entry)
+			worker->flags &= ~WORKER_REBIND;
+
+	wake_up_all(&gcwq->rebind_hold);
 }
 
 static struct worker *alloc_worker(void)

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/2] workqueue: fix possible deadlock in idle worker rebinding
  2012-09-05  6:12 [PATCH 1/2] workqueue: move WORKER_REBIND clearing in rebind_workers() to the end of the function Tejun Heo
@ 2012-09-05  6:16 ` Tejun Heo
  2012-09-05 23:11   ` Tejun Heo
  0 siblings, 1 reply; 4+ messages in thread
From: Tejun Heo @ 2012-09-05  6:16 UTC (permalink / raw)
  To: Lai Jiangshan, linux-kernel

Currently, rebind_workers() and idle_worker_rebind() are two-way
interlocked.  rebind_workers() waits for idle workers to finish
rebinding and rebound idle workers wait for rebind_workers() to finish
rebinding busy workers before proceeding.

Unfortunately, this isn't enough.  The second wait from idle workers
is implemented as follows.

	wait_event(gcwq->rebind_hold, !(worker->flags & WORKER_REBIND));

rebind_workers() clears WORKER_REBIND, wakes up the idle workers and
then returns.  If CPU hotplug cycle happens again before one of the
idle workers finishes the above wait_event(), rebind_workers() will
repeat the first part of the handshake - set WORKER_REBIND again and
wait for the idle worker to finish rebinding - and this leads to
deadlock because the idle worker would be waiting for WORKER_REBIND to
clear.

This is fixed by adding another interlocking step at the end -
rebind_workers() now waits for all the idle workers to finish the
above WORKER_REBIND wait before returning.  This ensures that all
rebinding steps are complete on all idle workers before the next
hotplug cycle can happen.

This problem was diagnosed by Lai Jiangshan who also posted a patch to
fix the issue, upon which this patch is based.

Note that this enables further simplification - we now can use
completion for the second handshake and WORKER_REBIND can be cleared
from idle workers themselves.  Further patches will follow.

Signed-off-by: Tejun Heo <tj@kernel.org>
Original-patch-by: Lai Jiangshan <laijs@cn.fujitsu.com>
LKML-Reference: <1346516916-1991-3-git-send-email-laijs@cn.fujitsu.com>
---
Lai, you deserve full credit for this and, if you're okay with it, I'd
like to mark you as the author with footnote explaining the
reimplementation.  Would that be okay?  Or do you prefer the above
Original-patch-by?

Thanks.

 kernel/workqueue.c |   29 +++++++++++++++++++++++++++--
 1 file changed, 27 insertions(+), 2 deletions(-)

--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -1326,6 +1326,15 @@ static void idle_worker_rebind(struct wo
 
 	/* we did our part, wait for rebind_workers() to finish up */
 	wait_event(gcwq->rebind_hold, !(worker->flags & WORKER_REBIND));
+
+	/*
+	 * rebind_workers() shouldn't finish until all workers passed the
+	 * above WORKER_REBIND wait.  Tell it when done.
+	 */
+	spin_lock_irq(&worker->pool->gcwq->lock);
+	if (!--worker->idle_rebind->cnt)
+		complete(&worker->idle_rebind->done);
+	spin_unlock_irq(&worker->pool->gcwq->lock);
 }
 
 /*
@@ -1448,12 +1457,28 @@ retry:
 	 * be cleared inside idle_worker_rebind().  Clear and release.
 	 * Clearing %WORKER_REBIND from this foreign context is safe
 	 * because these workers are still guaranteed to be idle.
+	 *
+	 * We need to make sure all idle workers passed WORKER_REBIND wait
+	 * in idle_worker_rebind() before returning; otherwise, workers can
+	 * get stuck at the wait if hotplug cycle repeats.
 	 */
-	for_each_worker_pool(pool, gcwq)
-		list_for_each_entry(worker, &pool->idle_list, entry)
+	idle_rebind.cnt = 1;
+	INIT_COMPLETION(idle_rebind.done);
+
+	for_each_worker_pool(pool, gcwq) {
+		list_for_each_entry(worker, &pool->idle_list, entry) {
 			worker->flags &= ~WORKER_REBIND;
+			idle_rebind.cnt++;
+		}
+	}
 
 	wake_up_all(&gcwq->rebind_hold);
+
+	if (--idle_rebind.cnt) {
+		spin_unlock_irq(&gcwq->lock);
+		wait_for_completion(&idle_rebind.done);
+		spin_lock_irq(&gcwq->lock);
+	}
 }
 
 static struct worker *alloc_worker(void)

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 2/2] workqueue: fix possible deadlock in idle worker rebinding
  2012-09-05  6:16 ` [PATCH 2/2] workqueue: fix possible deadlock in idle worker rebinding Tejun Heo
@ 2012-09-05 23:11   ` Tejun Heo
  2012-09-06  0:47     ` Lai Jiangshan
  0 siblings, 1 reply; 4+ messages in thread
From: Tejun Heo @ 2012-09-05 23:11 UTC (permalink / raw)
  To: Lai Jiangshan, linux-kernel

On Tue, Sep 04, 2012 at 11:16:32PM -0700, Tejun Heo wrote:
> Currently, rebind_workers() and idle_worker_rebind() are two-way
> interlocked.  rebind_workers() waits for idle workers to finish
> rebinding and rebound idle workers wait for rebind_workers() to finish
> rebinding busy workers before proceeding.

Applied to wq/for-3.6-fixes.

Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 2/2] workqueue: fix possible deadlock in idle worker rebinding
  2012-09-05 23:11   ` Tejun Heo
@ 2012-09-06  0:47     ` Lai Jiangshan
  0 siblings, 0 replies; 4+ messages in thread
From: Lai Jiangshan @ 2012-09-06  0:47 UTC (permalink / raw)
  To: Tejun Heo; +Cc: linux-kernel

On 09/06/2012 07:11 AM, Tejun Heo wrote:
> On Tue, Sep 04, 2012 at 11:16:32PM -0700, Tejun Heo wrote:
>> Currently, rebind_workers() and idle_worker_rebind() are two-way
>> interlocked.  rebind_workers() waits for idle workers to finish
>> rebinding and rebound idle workers wait for rebind_workers() to finish
>> rebinding busy workers before proceeding.
> 
> Applied to wq/for-3.6-fixes.
> 
> Thanks.
> 

OK for me.

Thanks.
Lai

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2012-09-06  1:44 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-09-05  6:12 [PATCH 1/2] workqueue: move WORKER_REBIND clearing in rebind_workers() to the end of the function Tejun Heo
2012-09-05  6:16 ` [PATCH 2/2] workqueue: fix possible deadlock in idle worker rebinding Tejun Heo
2012-09-05 23:11   ` Tejun Heo
2012-09-06  0:47     ` Lai Jiangshan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).