From baf59022c37d43f202e62d5130e4bac5e825b426 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 16 Sep 2010 10:42:16 +0200 Subject: [PATCH] workqueue: factor out start_flush_work() Factor out start_flush_work() from flush_work(). start_flush_work() has @wait_executing argument which controls whether the barrier is queued only if the work is pending or also if executing. As flush_work() needs to wait for execution too, it uses %true. This commit doesn't cause any behavior difference. start_flush_work() will be used to implement flush_work_sync(). Signed-off-by: Tejun Heo --- kernel/workqueue.c | 86 ++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 1240b9d94b03..33d31d768706 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -2326,6 +2326,48 @@ out_unlock: } EXPORT_SYMBOL_GPL(flush_workqueue); +static bool start_flush_work(struct work_struct *work, struct wq_barrier *barr, + bool wait_executing) +{ + struct worker *worker = NULL; + struct global_cwq *gcwq; + struct cpu_workqueue_struct *cwq; + + might_sleep(); + gcwq = get_work_gcwq(work); + if (!gcwq) + return false; + + spin_lock_irq(&gcwq->lock); + if (!list_empty(&work->entry)) { + /* + * See the comment near try_to_grab_pending()->smp_rmb(). + * If it was re-queued to a different gcwq under us, we + * are not going to wait. + */ + smp_rmb(); + cwq = get_work_cwq(work); + if (unlikely(!cwq || gcwq != cwq->gcwq)) + goto already_gone; + } else if (wait_executing) { + worker = find_worker_executing_work(gcwq, work); + if (!worker) + goto already_gone; + cwq = worker->current_cwq; + } else + goto already_gone; + + insert_wq_barrier(cwq, barr, work, worker); + spin_unlock_irq(&gcwq->lock); + + lock_map_acquire(&cwq->wq->lockdep_map); + lock_map_release(&cwq->wq->lockdep_map); + return true; +already_gone: + spin_unlock_irq(&gcwq->lock); + return false; +} + /** * flush_work - wait for a work to finish executing the last queueing instance * @work: the work to flush @@ -2346,46 +2388,14 @@ EXPORT_SYMBOL_GPL(flush_workqueue); */ bool flush_work(struct work_struct *work) { - struct worker *worker = NULL; - struct global_cwq *gcwq; - struct cpu_workqueue_struct *cwq; struct wq_barrier barr; - might_sleep(); - gcwq = get_work_gcwq(work); - if (!gcwq) - return 0; - - spin_lock_irq(&gcwq->lock); - if (!list_empty(&work->entry)) { - /* - * See the comment near try_to_grab_pending()->smp_rmb(). - * If it was re-queued to a different gcwq under us, we - * are not going to wait. - */ - smp_rmb(); - cwq = get_work_cwq(work); - if (unlikely(!cwq || gcwq != cwq->gcwq)) - goto already_gone; - } else { - worker = find_worker_executing_work(gcwq, work); - if (!worker) - goto already_gone; - cwq = worker->current_cwq; - } - - insert_wq_barrier(cwq, &barr, work, worker); - spin_unlock_irq(&gcwq->lock); - - lock_map_acquire(&cwq->wq->lockdep_map); - lock_map_release(&cwq->wq->lockdep_map); - - wait_for_completion(&barr.done); - destroy_work_on_stack(&barr.work); - return true; -already_gone: - spin_unlock_irq(&gcwq->lock); - return false; + if (start_flush_work(work, &barr, true)) { + wait_for_completion(&barr.done); + destroy_work_on_stack(&barr.work); + return true; + } else + return false; } EXPORT_SYMBOL_GPL(flush_work);