aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/workqueue.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2012-08-13 20:08:19 -0400
committerTejun Heo <tj@kernel.org>2012-08-13 20:08:19 -0400
commit23657bb192f14b789e4c478def8f11ecc95b4f6c (patch)
tree53d92c542c219d60ccef2ada045235f5e7076863 /kernel/workqueue.c
parent1265057fa02c7bed3b6d9ddc8a2048065a370364 (diff)
workqueue: add missing wmb() in clear_work_data()
Any operation which clears PENDING should be preceded by a wmb to guarantee that the next PENDING owner sees all the changes made before PENDING release. There are only two places where PENDING is cleared - set_work_cpu_and_clear_pending() and clear_work_data(). The caller of the former already does smp_wmb() but the latter doesn't have any. Move the wmb above set_work_cpu_and_clear_pending() into it and add one to clear_work_data(). There hasn't been any report related to this issue, and, given how clear_work_data() is used, it is extremely unlikely to have caused any actual problems on any architecture. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Oleg Nesterov <oleg@redhat.com>
Diffstat (limited to 'kernel/workqueue.c')
-rw-r--r--kernel/workqueue.c19
1 files changed, 12 insertions, 7 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 11723c5b2b20..4fef9527a620 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -570,11 +570,19 @@ static void set_work_cwq(struct work_struct *work,
570static void set_work_cpu_and_clear_pending(struct work_struct *work, 570static void set_work_cpu_and_clear_pending(struct work_struct *work,
571 unsigned int cpu) 571 unsigned int cpu)
572{ 572{
573 /*
574 * The following wmb is paired with the implied mb in
575 * test_and_set_bit(PENDING) and ensures all updates to @work made
576 * here are visible to and precede any updates by the next PENDING
577 * owner.
578 */
579 smp_wmb();
573 set_work_data(work, (unsigned long)cpu << WORK_OFFQ_CPU_SHIFT, 0); 580 set_work_data(work, (unsigned long)cpu << WORK_OFFQ_CPU_SHIFT, 0);
574} 581}
575 582
576static void clear_work_data(struct work_struct *work) 583static void clear_work_data(struct work_struct *work)
577{ 584{
585 smp_wmb(); /* see set_work_cpu_and_clear_pending() */
578 set_work_data(work, WORK_STRUCT_NO_CPU, 0); 586 set_work_data(work, WORK_STRUCT_NO_CPU, 0);
579} 587}
580 588
@@ -2182,14 +2190,11 @@ __acquires(&gcwq->lock)
2182 wake_up_worker(pool); 2190 wake_up_worker(pool);
2183 2191
2184 /* 2192 /*
2185 * Record the last CPU and clear PENDING. The following wmb is 2193 * Record the last CPU and clear PENDING which should be the last
2186 * paired with the implied mb in test_and_set_bit(PENDING) and 2194 * update to @work. Also, do this inside @gcwq->lock so that
2187 * ensures all updates to @work made here are visible to and 2195 * PENDING and queued state changes happen together while IRQ is
2188 * precede any updates by the next PENDING owner. Also, clear 2196 * disabled.
2189 * PENDING inside @gcwq->lock so that PENDING and queued state
2190 * changes happen together while IRQ is disabled.
2191 */ 2197 */
2192 smp_wmb();
2193 set_work_cpu_and_clear_pending(work, gcwq->cpu); 2198 set_work_cpu_and_clear_pending(work, gcwq->cpu);
2194 2199
2195 spin_unlock_irq(&gcwq->lock); 2200 spin_unlock_irq(&gcwq->lock);