Skip to content

Commit d3e3c10

Browse files
committed
io-wq: serialize hash clear with wakeup
We need to ensure that we serialize the stalled and hash bits with the wait_queue wait handler, or we could be racing with someone modifying the hashed state after we find it busy, but before we then give up and wait for it to be cleared. This can cause random delays or stalls when handling buffered writes for many files, where some of these files cause hash collisions between the worker threads. Cc: [email protected] Reported-by: Daniel Black <[email protected]> Fixes: e941894 ("io-wq: make buffered file write hashed work map per-ctx") Signed-off-by: Jens Axboe <[email protected]>
1 parent bad119b commit d3e3c10

File tree

1 file changed

+15
-2
lines changed

1 file changed

+15
-2
lines changed

fs/io-wq.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,19 +423,22 @@ static inline unsigned int io_get_work_hash(struct io_wq_work *work)
423423
return work->flags >> IO_WQ_HASH_SHIFT;
424424
}
425425

426-
static void io_wait_on_hash(struct io_wqe *wqe, unsigned int hash)
426+
static bool io_wait_on_hash(struct io_wqe *wqe, unsigned int hash)
427427
{
428428
struct io_wq *wq = wqe->wq;
429+
bool ret = false;
429430

430431
spin_lock_irq(&wq->hash->wait.lock);
431432
if (list_empty(&wqe->wait.entry)) {
432433
__add_wait_queue(&wq->hash->wait, &wqe->wait);
433434
if (!test_bit(hash, &wq->hash->map)) {
434435
__set_current_state(TASK_RUNNING);
435436
list_del_init(&wqe->wait.entry);
437+
ret = true;
436438
}
437439
}
438440
spin_unlock_irq(&wq->hash->wait.lock);
441+
return ret;
439442
}
440443

441444
static struct io_wq_work *io_get_next_work(struct io_wqe_acct *acct,
@@ -475,14 +478,21 @@ static struct io_wq_work *io_get_next_work(struct io_wqe_acct *acct,
475478
}
476479

477480
if (stall_hash != -1U) {
481+
bool unstalled;
482+
478483
/*
479484
* Set this before dropping the lock to avoid racing with new
480485
* work being added and clearing the stalled bit.
481486
*/
482487
set_bit(IO_ACCT_STALLED_BIT, &acct->flags);
483488
raw_spin_unlock(&wqe->lock);
484-
io_wait_on_hash(wqe, stall_hash);
489+
unstalled = io_wait_on_hash(wqe, stall_hash);
485490
raw_spin_lock(&wqe->lock);
491+
if (unstalled) {
492+
clear_bit(IO_ACCT_STALLED_BIT, &acct->flags);
493+
if (wq_has_sleeper(&wqe->wq->hash->wait))
494+
wake_up(&wqe->wq->hash->wait);
495+
}
486496
}
487497

488498
return NULL;
@@ -564,8 +574,11 @@ static void io_worker_handle_work(struct io_worker *worker)
564574
io_wqe_enqueue(wqe, linked);
565575

566576
if (hash != -1U && !next_hashed) {
577+
/* serialize hash clear with wake_up() */
578+
spin_lock_irq(&wq->hash->wait.lock);
567579
clear_bit(hash, &wq->hash->map);
568580
clear_bit(IO_ACCT_STALLED_BIT, &acct->flags);
581+
spin_unlock_irq(&wq->hash->wait.lock);
569582
if (wq_has_sleeper(&wq->hash->wait))
570583
wake_up(&wq->hash->wait);
571584
raw_spin_lock(&wqe->lock);

0 commit comments

Comments
 (0)