aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorLai Jiangshan <laijs@cn.fujitsu.com>2012-09-01 12:28:19 -0400
committerTejun Heo <tj@kernel.org>2012-09-04 20:04:45 -0400
commit96e65306b81351b656835c15931d1d237b252f27 (patch)
treeaf06187bebae44b48ca8e68a639a4ddc6b0a3509 /kernel
parent0d7614f09c1ebdbaa1599a5aba7593f147bf96ee (diff)
workqueue: UNBOUND -> REBIND morphing in rebind_workers() should be atomic
The compiler may compile the following code into TWO write/modify instructions. worker->flags &= ~WORKER_UNBOUND; worker->flags |= WORKER_REBIND; so the other CPU may temporarily see worker->flags which doesn't have either WORKER_UNBOUND or WORKER_REBIND set and perform local wakeup prematurely. Fix it by using single explicit assignment via ACCESS_ONCE(). Because idle workers have another WORKER_NOT_RUNNING flag, this bug doesn't exist for them; however, update it to use the same pattern for consistency. tj: Applied the change to idle workers too and updated comments and patch description a bit. Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> Signed-off-by: Tejun Heo <tj@kernel.org> Cc: stable@vger.kernel.org
Diffstat (limited to 'kernel')
-rw-r--r--kernel/workqueue.c17
1 files changed, 11 insertions, 6 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 692d97628a10..c462cd60c374 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -1396,12 +1396,15 @@ retry:
1396 /* set REBIND and kick idle ones, we'll wait for these later */ 1396 /* set REBIND and kick idle ones, we'll wait for these later */
1397 for_each_worker_pool(pool, gcwq) { 1397 for_each_worker_pool(pool, gcwq) {
1398 list_for_each_entry(worker, &pool->idle_list, entry) { 1398 list_for_each_entry(worker, &pool->idle_list, entry) {
1399 unsigned long worker_flags = worker->flags;
1400
1399 if (worker->flags & WORKER_REBIND) 1401 if (worker->flags & WORKER_REBIND)
1400 continue; 1402 continue;
1401 1403
1402 /* morph UNBOUND to REBIND */ 1404 /* morph UNBOUND to REBIND atomically */
1403 worker->flags &= ~WORKER_UNBOUND; 1405 worker_flags &= ~WORKER_UNBOUND;
1404 worker->flags |= WORKER_REBIND; 1406 worker_flags |= WORKER_REBIND;
1407 ACCESS_ONCE(worker->flags) = worker_flags;
1405 1408
1406 idle_rebind.cnt++; 1409 idle_rebind.cnt++;
1407 worker->idle_rebind = &idle_rebind; 1410 worker->idle_rebind = &idle_rebind;
@@ -1434,10 +1437,12 @@ retry:
1434 /* rebind busy workers */ 1437 /* rebind busy workers */
1435 for_each_busy_worker(worker, i, pos, gcwq) { 1438 for_each_busy_worker(worker, i, pos, gcwq) {
1436 struct work_struct *rebind_work = &worker->rebind_work; 1439 struct work_struct *rebind_work = &worker->rebind_work;
1440 unsigned long worker_flags = worker->flags;
1437 1441
1438 /* morph UNBOUND to REBIND */ 1442 /* morph UNBOUND to REBIND atomically */
1439 worker->flags &= ~WORKER_UNBOUND; 1443 worker_flags &= ~WORKER_UNBOUND;
1440 worker->flags |= WORKER_REBIND; 1444 worker_flags |= WORKER_REBIND;
1445 ACCESS_ONCE(worker->flags) = worker_flags;
1441 1446
1442 if (test_and_set_bit(WORK_STRUCT_PENDING_BIT, 1447 if (test_and_set_bit(WORK_STRUCT_PENDING_BIT,
1443 work_data_bits(rebind_work))) 1448 work_data_bits(rebind_work)))