]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
io-wq: fix wakeup race when adding new work
authorJens Axboe <axboe@kernel.dk>
Mon, 13 Sep 2021 15:20:44 +0000 (09:20 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 18 Sep 2021 11:41:48 +0000 (13:41 +0200)
commit 87df7fb922d18e96992aa5e824aa34b2065fef59 upstream.

When new work is added, io_wqe_enqueue() checks if we need to wake or
create a new worker. But that check is done outside the lock that
otherwise synchronizes us with a worker going to sleep, so we can end
up in the following situation:

CPU0 CPU1
lock
insert work
unlock
atomic_read(nr_running) != 0
lock
atomic_dec(nr_running)
no wakeup needed

Hold the wqe lock around the "need to wakeup" check. Then we can also get
rid of the temporary work_flags variable, as we know the work will remain
valid as long as we hold the lock.

Cc: stable@vger.kernel.org
Reported-by: Andres Freund <andres@anarazel.de>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/io-wq.c

index c7171d9758968a2f75f4d2c042f2f03bec6a546a..3de639ddb2d0d460d4b6374d9abb68c5b772f2bb 100644 (file)
@@ -798,7 +798,7 @@ append:
 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);
-       int work_flags;
+       bool do_wake;
        unsigned long flags;
 
        /*
@@ -811,14 +811,14 @@ static void io_wqe_enqueue(struct io_wqe *wqe, struct io_wq_work *work)
                return;
        }
 
-       work_flags = work->flags;
        raw_spin_lock_irqsave(&wqe->lock, flags);
        io_wqe_insert_work(wqe, work);
        wqe->flags &= ~IO_WQE_FLAG_STALLED;
+       do_wake = (work->flags & IO_WQ_WORK_CONCURRENT) ||
+                       !atomic_read(&acct->nr_running);
        raw_spin_unlock_irqrestore(&wqe->lock, flags);
 
-       if ((work_flags & IO_WQ_WORK_CONCURRENT) ||
-           !atomic_read(&acct->nr_running))
+       if (do_wake)
                io_wqe_wake_worker(wqe, acct);
 }