Commit 23657bb1 authored by Tejun Heo's avatar Tejun Heo

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: default avatarTejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
parent 1265057f
...@@ -570,11 +570,19 @@ static void set_work_cwq(struct work_struct *work, ...@@ -570,11 +570,19 @@ static void set_work_cwq(struct work_struct *work,
static void set_work_cpu_and_clear_pending(struct work_struct *work, static void set_work_cpu_and_clear_pending(struct work_struct *work,
unsigned int cpu) unsigned int cpu)
{ {
/*
* The following wmb is paired with the implied mb in
* test_and_set_bit(PENDING) and ensures all updates to @work made
* here are visible to and precede any updates by the next PENDING
* owner.
*/
smp_wmb();
set_work_data(work, (unsigned long)cpu << WORK_OFFQ_CPU_SHIFT, 0); set_work_data(work, (unsigned long)cpu << WORK_OFFQ_CPU_SHIFT, 0);
} }
static void clear_work_data(struct work_struct *work) static void clear_work_data(struct work_struct *work)
{ {
smp_wmb(); /* see set_work_cpu_and_clear_pending() */
set_work_data(work, WORK_STRUCT_NO_CPU, 0); set_work_data(work, WORK_STRUCT_NO_CPU, 0);
} }
...@@ -2182,14 +2190,11 @@ __acquires(&gcwq->lock) ...@@ -2182,14 +2190,11 @@ __acquires(&gcwq->lock)
wake_up_worker(pool); wake_up_worker(pool);
/* /*
* Record the last CPU and clear PENDING. The following wmb is * Record the last CPU and clear PENDING which should be the last
* paired with the implied mb in test_and_set_bit(PENDING) and * update to @work. Also, do this inside @gcwq->lock so that
* ensures all updates to @work made here are visible to and * PENDING and queued state changes happen together while IRQ is
* precede any updates by the next PENDING owner. Also, clear * disabled.
* PENDING inside @gcwq->lock so that PENDING and queued state
* changes happen together while IRQ is disabled.
*/ */
smp_wmb();
set_work_cpu_and_clear_pending(work, gcwq->cpu); set_work_cpu_and_clear_pending(work, gcwq->cpu);
spin_unlock_irq(&gcwq->lock); spin_unlock_irq(&gcwq->lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment