diff options
| author | Tejun Heo <tj@kernel.org> | 2012-08-13 20:08:19 -0400 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2012-08-13 20:08:19 -0400 |
| commit | 23657bb192f14b789e4c478def8f11ecc95b4f6c (patch) | |
| tree | 53d92c542c219d60ccef2ada045235f5e7076863 /kernel/workqueue.c | |
| parent | 1265057fa02c7bed3b6d9ddc8a2048065a370364 (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.c | 19 |
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, | |||
| 570 | static void set_work_cpu_and_clear_pending(struct work_struct *work, | 570 | static 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 | ||
| 576 | static void clear_work_data(struct work_struct *work) | 583 | static 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); |
