Merge branch 'misc.namei' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux-2.6-microblaze.git] / fs / io-wq.c
index d80e4a7..6c55362 100644 (file)
@@ -709,6 +709,7 @@ static void create_worker_cont(struct callback_head *cb)
                }
                raw_spin_unlock(&wqe->lock);
                io_worker_ref_put(wqe->wq);
+               kfree(worker);
                return;
        }
 
@@ -725,6 +726,7 @@ static void io_workqueue_create(struct work_struct *work)
        if (!io_queue_worker_create(worker, acct, create_worker_cont)) {
                clear_bit_unlock(0, &worker->create_state);
                io_worker_release(worker);
+               kfree(worker);
        }
 }
 
@@ -759,6 +761,7 @@ fail:
        if (!IS_ERR(tsk)) {
                io_init_new_worker(wqe, worker, tsk);
        } else if (!io_should_retry_thread(PTR_ERR(tsk))) {
+               kfree(worker);
                goto fail;
        } else {
                INIT_WORK(&worker->work, io_workqueue_create);
@@ -832,6 +835,11 @@ append:
        wq_list_add_after(&work->list, &tail->list, &acct->work_list);
 }
 
+static bool io_wq_work_match_item(struct io_wq_work *work, void *data)
+{
+       return work == data;
+}
+
 static void io_wqe_enqueue(struct io_wqe *wqe, struct io_wq_work *work)
 {
        struct io_wqe_acct *acct = io_work_get_acct(wqe, work);
@@ -844,7 +852,6 @@ static void io_wqe_enqueue(struct io_wqe *wqe, struct io_wq_work *work)
         */
        if (test_bit(IO_WQ_BIT_EXIT, &wqe->wq->state) ||
            (work->flags & IO_WQ_WORK_CANCEL)) {
-run_cancel:
                io_run_cancel(work, wqe);
                return;
        }
@@ -864,15 +871,22 @@ run_cancel:
                bool did_create;
 
                did_create = io_wqe_create_worker(wqe, acct);
-               if (unlikely(!did_create)) {
-                       raw_spin_lock(&wqe->lock);
-                       /* fatal condition, failed to create the first worker */
-                       if (!acct->nr_workers) {
-                               raw_spin_unlock(&wqe->lock);
-                               goto run_cancel;
-                       }
-                       raw_spin_unlock(&wqe->lock);
+               if (likely(did_create))
+                       return;
+
+               raw_spin_lock(&wqe->lock);
+               /* fatal condition, failed to create the first worker */
+               if (!acct->nr_workers) {
+                       struct io_cb_cancel_data match = {
+                               .fn             = io_wq_work_match_item,
+                               .data           = work,
+                               .cancel_all     = false,
+                       };
+
+                       if (io_acct_cancel_pending_work(wqe, acct, &match))
+                               raw_spin_lock(&wqe->lock);
                }
+               raw_spin_unlock(&wqe->lock);
        }
 }
 
@@ -1122,7 +1136,7 @@ static bool io_task_work_match(struct callback_head *cb, void *data)
 {
        struct io_worker *worker;
 
-       if (cb->func != create_worker_cb || cb->func != create_worker_cont)
+       if (cb->func != create_worker_cb && cb->func != create_worker_cont)
                return false;
        worker = container_of(cb, struct io_worker, create_work);
        return worker->wqe->wq == data;
@@ -1143,9 +1157,14 @@ static void io_wq_exit_workers(struct io_wq *wq)
 
        while ((cb = task_work_cancel_match(wq->task, io_task_work_match, wq)) != NULL) {
                struct io_worker *worker;
+               struct io_wqe_acct *acct;
 
                worker = container_of(cb, struct io_worker, create_work);
-               atomic_dec(&worker->wqe->acct[worker->create_index].nr_running);
+               acct = io_wqe_get_acct(worker);
+               atomic_dec(&acct->nr_running);
+               raw_spin_lock(&worker->wqe->lock);
+               acct->nr_workers--;
+               raw_spin_unlock(&worker->wqe->lock);
                io_worker_ref_put(wq);
                clear_bit_unlock(0, &worker->create_state);
                io_worker_release(worker);