Merge branch 'core-rcu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.git] / kernel / workqueue.c
index e501c79e283adc5cd5df4ab7d87696a0e74acdde..bc88fd939f4e72c43de15ad96c42aa2fdce4a996 100644 (file)
@@ -248,7 +248,7 @@ struct workqueue_struct {
        struct list_head        flusher_overflow; /* WQ: flush overflow list */
 
        struct list_head        maydays;        /* MD: pwqs requesting rescue */
-       struct worker           *rescuer;       /* I: rescue worker */
+       struct worker           *rescuer;       /* MD: rescue worker */
 
        int                     nr_drainers;    /* WQ: drain in progress */
        int                     saved_max_active; /* WQ: saved pwq max_active */
@@ -355,6 +355,7 @@ EXPORT_SYMBOL_GPL(system_freezable_power_efficient_wq);
 
 static int worker_thread(void *__worker);
 static void workqueue_sysfs_unregister(struct workqueue_struct *wq);
+static void show_pwq(struct pool_workqueue *pwq);
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/workqueue.h>
@@ -2526,8 +2527,14 @@ repeat:
                         */
                        if (need_to_create_worker(pool)) {
                                spin_lock(&wq_mayday_lock);
-                               get_pwq(pwq);
-                               list_move_tail(&pwq->mayday_node, &wq->maydays);
+                               /*
+                                * Queue iff we aren't racing destruction
+                                * and somebody else hasn't queued it already.
+                                */
+                               if (wq->rescuer && list_empty(&pwq->mayday_node)) {
+                                       get_pwq(pwq);
+                                       list_add_tail(&pwq->mayday_node, &wq->maydays);
+                               }
                                spin_unlock(&wq_mayday_lock);
                        }
                }
@@ -4308,6 +4315,22 @@ err_destroy:
 }
 EXPORT_SYMBOL_GPL(alloc_workqueue);
 
+static bool pwq_busy(struct pool_workqueue *pwq)
+{
+       int i;
+
+       for (i = 0; i < WORK_NR_COLORS; i++)
+               if (pwq->nr_in_flight[i])
+                       return true;
+
+       if ((pwq != pwq->wq->dfl_pwq) && (pwq->refcnt > 1))
+               return true;
+       if (pwq->nr_active || !list_empty(&pwq->delayed_works))
+               return true;
+
+       return false;
+}
+
 /**
  * destroy_workqueue - safely terminate a workqueue
  * @wq: target workqueue
@@ -4319,31 +4342,51 @@ void destroy_workqueue(struct workqueue_struct *wq)
        struct pool_workqueue *pwq;
        int node;
 
+       /*
+        * Remove it from sysfs first so that sanity check failure doesn't
+        * lead to sysfs name conflicts.
+        */
+       workqueue_sysfs_unregister(wq);
+
        /* drain it before proceeding with destruction */
        drain_workqueue(wq);
 
-       /* sanity checks */
-       mutex_lock(&wq->mutex);
-       for_each_pwq(pwq, wq) {
-               int i;
+       /* kill rescuer, if sanity checks fail, leave it w/o rescuer */
+       if (wq->rescuer) {
+               struct worker *rescuer = wq->rescuer;
 
-               for (i = 0; i < WORK_NR_COLORS; i++) {
-                       if (WARN_ON(pwq->nr_in_flight[i])) {
-                               mutex_unlock(&wq->mutex);
-                               show_workqueue_state();
-                               return;
-                       }
-               }
+               /* this prevents new queueing */
+               spin_lock_irq(&wq_mayday_lock);
+               wq->rescuer = NULL;
+               spin_unlock_irq(&wq_mayday_lock);
+
+               /* rescuer will empty maydays list before exiting */
+               kthread_stop(rescuer->task);
+               kfree(rescuer);
+       }
 
-               if (WARN_ON((pwq != wq->dfl_pwq) && (pwq->refcnt > 1)) ||
-                   WARN_ON(pwq->nr_active) ||
-                   WARN_ON(!list_empty(&pwq->delayed_works))) {
+       /*
+        * Sanity checks - grab all the locks so that we wait for all
+        * in-flight operations which may do put_pwq().
+        */
+       mutex_lock(&wq_pool_mutex);
+       mutex_lock(&wq->mutex);
+       for_each_pwq(pwq, wq) {
+               spin_lock_irq(&pwq->pool->lock);
+               if (WARN_ON(pwq_busy(pwq))) {
+                       pr_warning("%s: %s has the following busy pwq\n",
+                                  __func__, wq->name);
+                       show_pwq(pwq);
+                       spin_unlock_irq(&pwq->pool->lock);
                        mutex_unlock(&wq->mutex);
+                       mutex_unlock(&wq_pool_mutex);
                        show_workqueue_state();
                        return;
                }
+               spin_unlock_irq(&pwq->pool->lock);
        }
        mutex_unlock(&wq->mutex);
+       mutex_unlock(&wq_pool_mutex);
 
        /*
         * wq list is used to freeze wq, remove from list after
@@ -4353,11 +4396,6 @@ void destroy_workqueue(struct workqueue_struct *wq)
        list_del_rcu(&wq->list);
        mutex_unlock(&wq_pool_mutex);
 
-       workqueue_sysfs_unregister(wq);
-
-       if (wq->rescuer)
-               kthread_stop(wq->rescuer->task);
-
        if (!(wq->flags & WQ_UNBOUND)) {
                wq_unregister_lockdep(wq);
                /*
@@ -4632,7 +4670,8 @@ static void show_pwq(struct pool_workqueue *pwq)
        pr_info("  pwq %d:", pool->id);
        pr_cont_pool_info(pool);
 
-       pr_cont(" active=%d/%d%s\n", pwq->nr_active, pwq->max_active,
+       pr_cont(" active=%d/%d refcnt=%d%s\n",
+               pwq->nr_active, pwq->max_active, pwq->refcnt,
                !list_empty(&pwq->mayday_node) ? " MAYDAY" : "");
 
        hash_for_each(pool->busy_hash, bkt, worker, hentry) {
@@ -4651,7 +4690,7 @@ static void show_pwq(struct pool_workqueue *pwq)
 
                        pr_cont("%s %d%s:%ps", comma ? "," : "",
                                task_pid_nr(worker->task),
-                               worker == pwq->wq->rescuer ? "(RESCUER)" : "",
+                               worker->rescue_wq ? "(RESCUER)" : "",
                                worker->current_func);
                        list_for_each_entry(work, &worker->scheduled, entry)
                                pr_cont_work(false, work);