diff options
author | Tejun Heo <tj@kernel.org> | 2013-03-12 11:30:04 -0700 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2013-03-12 11:30:04 -0700 |
commit | 75ccf5950f828d53aebfd3a852283a00abf2c5bf (patch) | |
tree | 3a2085d3deec15d600cfed31107164d92600e078 /kernel/workqueue.c | |
parent | 8864b4e59f7945a636eeb27671f10486149be6e6 (diff) | |
download | lwn-75ccf5950f828d53aebfd3a852283a00abf2c5bf.tar.gz lwn-75ccf5950f828d53aebfd3a852283a00abf2c5bf.zip |
workqueue: prepare flush_workqueue() for dynamic creation and destrucion of unbound pool_workqueues
Unbound pwqs (pool_workqueues) will be dynamically created and
destroyed with the scheduled unbound workqueue w/ custom attributes
support. This patch synchronizes pwq linking and unlinking against
flush_workqueue() so that its operation isn't disturbed by pwqs coming
and going.
Linking and unlinking a pwq into wq->pwqs is now protected also by
wq->flush_mutex and a new pwq's work_color is initialized to
wq->work_color during linking. This ensures that pwqs changes don't
disturb flush_workqueue() in progress and the new pwq's work coloring
stays in sync with the rest of the workqueue.
flush_mutex during unlinking isn't strictly necessary but it's simpler
to do it anyway.
Signed-off-by: Tejun Heo <tj@kernel.org>
Reviewed-by: Lai Jiangshan <laijs@cn.fujitsu.com>
Diffstat (limited to 'kernel/workqueue.c')
-rw-r--r-- | kernel/workqueue.c | 25 |
1 files changed, 23 insertions, 2 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 7dd8e7bcec51..e933979678e5 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -122,6 +122,9 @@ enum { * W: workqueue_lock protected. * * R: workqueue_lock protected for writes. Sched-RCU protected for reads. + * + * FR: wq->flush_mutex and workqueue_lock protected for writes. Sched-RCU + * protected for reads. */ /* struct worker is defined in workqueue_internal.h */ @@ -185,7 +188,7 @@ struct pool_workqueue { int nr_active; /* L: nr of active works */ int max_active; /* L: max active works */ struct list_head delayed_works; /* L: delayed works */ - struct list_head pwqs_node; /* R: node on wq->pwqs */ + struct list_head pwqs_node; /* FR: node on wq->pwqs */ struct list_head mayday_node; /* W: node on wq->maydays */ /* @@ -214,7 +217,7 @@ struct wq_flusher { struct workqueue_struct { unsigned int flags; /* W: WQ_* flags */ struct pool_workqueue __percpu *cpu_pwqs; /* I: per-cpu pwq's */ - struct list_head pwqs; /* R: all pwqs of this wq */ + struct list_head pwqs; /* FR: all pwqs of this wq */ struct list_head list; /* W: list of all workqueues */ struct mutex flush_mutex; /* protects wq flushing */ @@ -3402,9 +3405,16 @@ static void pwq_unbound_release_workfn(struct work_struct *work) if (WARN_ON_ONCE(!(wq->flags & WQ_UNBOUND))) return; + /* + * Unlink @pwq. Synchronization against flush_mutex isn't strictly + * necessary on release but do it anyway. It's easier to verify + * and consistent with the linking path. + */ + mutex_lock(&wq->flush_mutex); spin_lock_irq(&workqueue_lock); list_del_rcu(&pwq->pwqs_node); spin_unlock_irq(&workqueue_lock); + mutex_unlock(&wq->flush_mutex); put_unbound_pool(pool); call_rcu_sched(&pwq->rcu, rcu_free_pwq); @@ -3432,7 +3442,18 @@ static void init_and_link_pwq(struct pool_workqueue *pwq, INIT_LIST_HEAD(&pwq->mayday_node); INIT_WORK(&pwq->unbound_release_work, pwq_unbound_release_workfn); + /* + * Link @pwq and set the matching work_color. This is synchronized + * with flush_mutex to avoid confusing flush_workqueue(). + */ + mutex_lock(&wq->flush_mutex); + spin_lock_irq(&workqueue_lock); + + pwq->work_color = wq->work_color; list_add_tail_rcu(&pwq->pwqs_node, &wq->pwqs); + + spin_unlock_irq(&workqueue_lock); + mutex_unlock(&wq->flush_mutex); } static int alloc_and_link_pwqs(struct workqueue_struct *wq) |