diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/workqueue.c | 29 |
1 files changed, 27 insertions, 2 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c index d79a18d0c42e..dc7b8458e275 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c | |||
| @@ -1326,6 +1326,15 @@ static void idle_worker_rebind(struct worker *worker) | |||
| 1326 | 1326 | ||
| 1327 | /* we did our part, wait for rebind_workers() to finish up */ | 1327 | /* we did our part, wait for rebind_workers() to finish up */ |
| 1328 | wait_event(gcwq->rebind_hold, !(worker->flags & WORKER_REBIND)); | 1328 | wait_event(gcwq->rebind_hold, !(worker->flags & WORKER_REBIND)); |
| 1329 | |||
| 1330 | /* | ||
| 1331 | * rebind_workers() shouldn't finish until all workers passed the | ||
| 1332 | * above WORKER_REBIND wait. Tell it when done. | ||
| 1333 | */ | ||
| 1334 | spin_lock_irq(&worker->pool->gcwq->lock); | ||
| 1335 | if (!--worker->idle_rebind->cnt) | ||
| 1336 | complete(&worker->idle_rebind->done); | ||
| 1337 | spin_unlock_irq(&worker->pool->gcwq->lock); | ||
| 1329 | } | 1338 | } |
| 1330 | 1339 | ||
| 1331 | /* | 1340 | /* |
| @@ -1448,12 +1457,28 @@ retry: | |||
| 1448 | * be cleared inside idle_worker_rebind(). Clear and release. | 1457 | * be cleared inside idle_worker_rebind(). Clear and release. |
| 1449 | * Clearing %WORKER_REBIND from this foreign context is safe | 1458 | * Clearing %WORKER_REBIND from this foreign context is safe |
| 1450 | * because these workers are still guaranteed to be idle. | 1459 | * because these workers are still guaranteed to be idle. |
| 1460 | * | ||
| 1461 | * We need to make sure all idle workers passed WORKER_REBIND wait | ||
| 1462 | * in idle_worker_rebind() before returning; otherwise, workers can | ||
| 1463 | * get stuck at the wait if hotplug cycle repeats. | ||
| 1451 | */ | 1464 | */ |
| 1452 | for_each_worker_pool(pool, gcwq) | 1465 | idle_rebind.cnt = 1; |
| 1453 | list_for_each_entry(worker, &pool->idle_list, entry) | 1466 | INIT_COMPLETION(idle_rebind.done); |
| 1467 | |||
| 1468 | for_each_worker_pool(pool, gcwq) { | ||
| 1469 | list_for_each_entry(worker, &pool->idle_list, entry) { | ||
| 1454 | worker->flags &= ~WORKER_REBIND; | 1470 | worker->flags &= ~WORKER_REBIND; |
| 1471 | idle_rebind.cnt++; | ||
| 1472 | } | ||
| 1473 | } | ||
| 1455 | 1474 | ||
| 1456 | wake_up_all(&gcwq->rebind_hold); | 1475 | wake_up_all(&gcwq->rebind_hold); |
| 1476 | |||
| 1477 | if (--idle_rebind.cnt) { | ||
| 1478 | spin_unlock_irq(&gcwq->lock); | ||
| 1479 | wait_for_completion(&idle_rebind.done); | ||
| 1480 | spin_lock_irq(&gcwq->lock); | ||
| 1481 | } | ||
| 1457 | } | 1482 | } |
| 1458 | 1483 | ||
| 1459 | static struct worker *alloc_worker(void) | 1484 | static struct worker *alloc_worker(void) |
