diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/workqueue.c | 37 |
1 files changed, 36 insertions, 1 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 383548ed0b54..1e1373bcb3e3 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c | |||
@@ -1825,10 +1825,45 @@ static bool manage_workers(struct worker *worker) | |||
1825 | struct worker_pool *pool = worker->pool; | 1825 | struct worker_pool *pool = worker->pool; |
1826 | bool ret = false; | 1826 | bool ret = false; |
1827 | 1827 | ||
1828 | if (!mutex_trylock(&pool->manager_mutex)) | 1828 | if (pool->flags & POOL_MANAGING_WORKERS) |
1829 | return ret; | 1829 | return ret; |
1830 | 1830 | ||
1831 | pool->flags |= POOL_MANAGING_WORKERS; | 1831 | pool->flags |= POOL_MANAGING_WORKERS; |
1832 | |||
1833 | /* | ||
1834 | * To simplify both worker management and CPU hotplug, hold off | ||
1835 | * management while hotplug is in progress. CPU hotplug path can't | ||
1836 | * grab %POOL_MANAGING_WORKERS to achieve this because that can | ||
1837 | * lead to idle worker depletion (all become busy thinking someone | ||
1838 | * else is managing) which in turn can result in deadlock under | ||
1839 | * extreme circumstances. Use @pool->manager_mutex to synchronize | ||
1840 | * manager against CPU hotplug. | ||
1841 | * | ||
1842 | * manager_mutex would always be free unless CPU hotplug is in | ||
1843 | * progress. trylock first without dropping @gcwq->lock. | ||
1844 | */ | ||
1845 | if (unlikely(!mutex_trylock(&pool->manager_mutex))) { | ||
1846 | spin_unlock_irq(&pool->gcwq->lock); | ||
1847 | mutex_lock(&pool->manager_mutex); | ||
1848 | /* | ||
1849 | * CPU hotplug could have happened while we were waiting | ||
1850 | * for manager_mutex. Hotplug itself can't handle us | ||
1851 | * because manager isn't either on idle or busy list, and | ||
1852 | * @gcwq's state and ours could have deviated. | ||
1853 | * | ||
1854 | * As hotplug is now excluded via manager_mutex, we can | ||
1855 | * simply try to bind. It will succeed or fail depending | ||
1856 | * on @gcwq's current state. Try it and adjust | ||
1857 | * %WORKER_UNBOUND accordingly. | ||
1858 | */ | ||
1859 | if (worker_maybe_bind_and_lock(worker)) | ||
1860 | worker->flags &= ~WORKER_UNBOUND; | ||
1861 | else | ||
1862 | worker->flags |= WORKER_UNBOUND; | ||
1863 | |||
1864 | ret = true; | ||
1865 | } | ||
1866 | |||
1832 | pool->flags &= ~POOL_MANAGE_WORKERS; | 1867 | pool->flags &= ~POOL_MANAGE_WORKERS; |
1833 | 1868 | ||
1834 | /* | 1869 | /* |