io-wq: fix IO_WQ_WORK_NO_CANCEL cancellation
authorPavel Begunkov <asml.silence@gmail.com>
Sun, 1 Mar 2020 16:18:19 +0000 (19:18 +0300)
committerJens Axboe <axboe@kernel.dk>
Mon, 2 Mar 2020 14:20:08 +0000 (07:20 -0700)
To cancel a work, io-wq sets IO_WQ_WORK_CANCEL and executes the
callback. However, IO_WQ_WORK_NO_CANCEL works will just execute and may
return next work, which will be ignored and lost.

Cancel the whole link.

Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/io-wq.c

index bf8ed1b..9a7aacc 100644 (file)
@@ -747,6 +747,17 @@ static bool io_wq_can_queue(struct io_wqe *wqe, struct io_wqe_acct *acct,
        return true;
 }
 
+static void io_run_cancel(struct io_wq_work *work)
+{
+       do {
+               struct io_wq_work *old_work = work;
+
+               work->flags |= IO_WQ_WORK_CANCEL;
+               work->func(&work);
+               work = (work == old_work) ? NULL : work;
+       } while (work);
+}
+
 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);
@@ -760,8 +771,7 @@ static void io_wqe_enqueue(struct io_wqe *wqe, struct io_wq_work *work)
         * It's close enough to not be an issue, fork() has the same delay.
         */
        if (unlikely(!io_wq_can_queue(wqe, acct, work))) {
-               work->flags |= IO_WQ_WORK_CANCEL;
-               work->func(&work);
+               io_run_cancel(work);
                return;
        }
 
@@ -900,8 +910,7 @@ static enum io_wq_cancel io_wqe_cancel_cb_work(struct io_wqe *wqe,
        spin_unlock_irqrestore(&wqe->lock, flags);
 
        if (found) {
-               work->flags |= IO_WQ_WORK_CANCEL;
-               work->func(&work);
+               io_run_cancel(work);
                return IO_WQ_CANCEL_OK;
        }
 
@@ -976,8 +985,7 @@ static enum io_wq_cancel io_wqe_cancel_work(struct io_wqe *wqe,
        spin_unlock_irqrestore(&wqe->lock, flags);
 
        if (found) {
-               work->flags |= IO_WQ_WORK_CANCEL;
-               work->func(&work);
+               io_run_cancel(work);
                return IO_WQ_CANCEL_OK;
        }