Merge tag 'v3.10.82' into update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / kernel / workqueue.c
index 650b563836801f41809d1e0170f9ffe0e14d1d3a..1c020389445b25383adc76d557aad8a75a5b2067 100644 (file)
@@ -1950,17 +1950,13 @@ static void pool_mayday_timeout(unsigned long __pool)
  * spin_lock_irq(pool->lock) which may be released and regrabbed
  * multiple times.  Does GFP_KERNEL allocations.  Called only from
  * manager.
- *
- * RETURNS:
- * %false if no action was taken and pool->lock stayed locked, %true
- * otherwise.
  */
-static bool maybe_create_worker(struct worker_pool *pool)
+static void maybe_create_worker(struct worker_pool *pool)
 __releases(&pool->lock)
 __acquires(&pool->lock)
 {
        if (!need_to_create_worker(pool))
-               return false;
+               return;
 restart:
        spin_unlock_irq(&pool->lock);
 
@@ -1977,7 +1973,7 @@ restart:
                        start_worker(worker);
                        if (WARN_ON_ONCE(need_to_create_worker(pool)))
                                goto restart;
-                       return true;
+                       return;
                }
 
                if (!need_to_create_worker(pool))
@@ -1994,7 +1990,7 @@ restart:
        spin_lock_irq(&pool->lock);
        if (need_to_create_worker(pool))
                goto restart;
-       return true;
+       return;
 }
 
 /**
@@ -2007,15 +2003,9 @@ restart:
  * LOCKING:
  * spin_lock_irq(pool->lock) which may be released and regrabbed
  * multiple times.  Called only from manager.
- *
- * RETURNS:
- * %false if no action was taken and pool->lock stayed locked, %true
- * otherwise.
  */
-static bool maybe_destroy_workers(struct worker_pool *pool)
+static void maybe_destroy_workers(struct worker_pool *pool)
 {
-       bool ret = false;
-
        while (too_many_workers(pool)) {
                struct worker *worker;
                unsigned long expires;
@@ -2029,10 +2019,7 @@ static bool maybe_destroy_workers(struct worker_pool *pool)
                }
 
                destroy_worker(worker);
-               ret = true;
        }
-
-       return ret;
 }
 
 /**
@@ -2052,13 +2039,14 @@ static bool maybe_destroy_workers(struct worker_pool *pool)
  * multiple times.  Does GFP_KERNEL allocations.
  *
  * RETURNS:
- * spin_lock_irq(pool->lock) which may be released and regrabbed
- * multiple times.  Does GFP_KERNEL allocations.
+ * %false if the pool doesn't need management and the caller can safely
+ * start processing works, %true if management function was performed and
+ * the conditions that the caller verified before calling the function may
+ * no longer be true.
  */
 static bool manage_workers(struct worker *worker)
 {
        struct worker_pool *pool = worker->pool;
-       bool ret = false;
 
        /*
         * Managership is governed by two mutexes - manager_arb and
@@ -2082,7 +2070,7 @@ static bool manage_workers(struct worker *worker)
         * manager_mutex.
         */
        if (!mutex_trylock(&pool->manager_arb))
-               return ret;
+               return false;
 
        /*
         * With manager arbitration won, manager_mutex would be free in
@@ -2092,7 +2080,6 @@ static bool manage_workers(struct worker *worker)
                spin_unlock_irq(&pool->lock);
                mutex_lock(&pool->manager_mutex);
                spin_lock_irq(&pool->lock);
-               ret = true;
        }
 
        pool->flags &= ~POOL_MANAGE_WORKERS;
@@ -2101,12 +2088,12 @@ static bool manage_workers(struct worker *worker)
         * Destroy and then create so that may_start_working() is true
         * on return.
         */
-       ret |= maybe_destroy_workers(pool);
-       ret |= maybe_create_worker(pool);
+       maybe_destroy_workers(pool);
+       maybe_create_worker(pool);
 
        mutex_unlock(&pool->manager_mutex);
        mutex_unlock(&pool->manager_arb);
-       return ret;
+       return true;
 }
 
 /**
@@ -2909,19 +2896,57 @@ bool flush_work(struct work_struct *work)
 }
 EXPORT_SYMBOL_GPL(flush_work);
 
+struct cwt_wait {
+       wait_queue_t            wait;
+       struct work_struct      *work;
+};
+
+static int cwt_wakefn(wait_queue_t *wait, unsigned mode, int sync, void *key)
+{
+       struct cwt_wait *cwait = container_of(wait, struct cwt_wait, wait);
+
+       if (cwait->work != key)
+               return 0;
+       return autoremove_wake_function(wait, mode, sync, key);
+}
+
 static bool __cancel_work_timer(struct work_struct *work, bool is_dwork)
 {
+       static DECLARE_WAIT_QUEUE_HEAD(cancel_waitq);
        unsigned long flags;
        int ret;
 
        do {
                ret = try_to_grab_pending(work, is_dwork, &flags);
                /*
-                * If someone else is canceling, wait for the same event it
-                * would be waiting for before retrying.
+                * If someone else is already canceling, wait for it to
+                * finish.  flush_work() doesn't work for PREEMPT_NONE
+                * because we may get scheduled between @work's completion
+                * and the other canceling task resuming and clearing
+                * CANCELING - flush_work() will return false immediately
+                * as @work is no longer busy, try_to_grab_pending() will
+                * return -ENOENT as @work is still being canceled and the
+                * other canceling task won't be able to clear CANCELING as
+                * we're hogging the CPU.
+                *
+                * Let's wait for completion using a waitqueue.  As this
+                * may lead to the thundering herd problem, use a custom
+                * wake function which matches @work along with exclusive
+                * wait and wakeup.
                 */
-               if (unlikely(ret == -ENOENT))
-                       flush_work(work);
+               if (unlikely(ret == -ENOENT)) {
+                       struct cwt_wait cwait;
+
+                       init_wait(&cwait.wait);
+                       cwait.wait.func = cwt_wakefn;
+                       cwait.work = work;
+
+                       prepare_to_wait_exclusive(&cancel_waitq, &cwait.wait,
+                                                 TASK_UNINTERRUPTIBLE);
+                       if (work_is_canceling(work))
+                               schedule();
+                       finish_wait(&cancel_waitq, &cwait.wait);
+               }
        } while (unlikely(ret < 0));
 
        /* tell other tasks trying to grab @work to back off */
@@ -2930,6 +2955,16 @@ static bool __cancel_work_timer(struct work_struct *work, bool is_dwork)
 
        flush_work(work);
        clear_work_data(work);
+
+       /*
+        * Paired with prepare_to_wait() above so that either
+        * waitqueue_active() is visible here or !work_is_canceling() is
+        * visible there.
+        */
+       smp_mb();
+       if (waitqueue_active(&cancel_waitq))
+               __wake_up(&cancel_waitq, TASK_NORMAL, 1, work);
+
        return ret;
 }