diff options
author | Jens Axboe <axboe@kernel.dk> | 2021-08-30 16:45:47 +0300 |
---|---|---|
committer | Jens Axboe <axboe@kernel.dk> | 2021-08-30 16:45:47 +0300 |
commit | 87df7fb922d18e96992aa5e824aa34b2065fef59 (patch) | |
tree | abb09fea0e8f9af2e14c127bb4f9b1018072e7d5 | |
parent | a9a4aa9fbfc5b87f315c63d9a317648774a46879 (diff) | |
download | linux-87df7fb922d18e96992aa5e824aa34b2065fef59.tar.xz |
io-wq: fix wakeup race when adding new work
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>
-rw-r--r-- | fs/io-wq.c | 8 |
1 files changed, 4 insertions, 4 deletions
diff --git a/fs/io-wq.c b/fs/io-wq.c index 13aeb48a0964..cd9bd095fb1b 100644 --- a/fs/io-wq.c +++ b/fs/io-wq.c @@ -794,7 +794,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; /* * If io-wq is exiting for this task, or if the request has explicitly @@ -806,14 +806,14 @@ static void io_wqe_enqueue(struct io_wqe *wqe, struct io_wq_work *work) return; } - work_flags = work->flags; raw_spin_lock(&wqe->lock); 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(&wqe->lock); - if ((work_flags & IO_WQ_WORK_CONCURRENT) || - !atomic_read(&acct->nr_running)) + if (do_wake) io_wqe_wake_worker(wqe, acct); } |