FAILED: patch "[PATCH] io-wq: serialize hash clear with wakeup" failed to apply to 5.14-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.14-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From d3e3c102d107bb84251455a298cf475f24bab995 Mon Sep 17 00:00:00 2001
From: Jens Axboe <[email protected]>
Date: Thu, 11 Nov 2021 17:32:53 -0700
Subject: [PATCH] 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.
fs/io-wq.c +15 -2
@@ -423,9 +423,10 @@ static inline unsigned int io_get_work_hash(struct io_wq_work *work)return work->flags >>IO_WQ_HASH_SHIFT ; } -static void io_wait_on_hash (struct io_wqe *wqe,unsigned int hash) +static bool io_wait_on_hash (struct io_wqe *wqe,unsigned int hash) {struct io_wq *wq = wqe->wq; +bool ret = false;spin_lock_irq (&wq->hash->wait.lock);if (list_empty (&wqe->wait.entry)) {
@@ -433,9 +434,11 @@ static void io_wait_on_hash(struct io_wqe *wqe, unsigned int hash)if (!test_bit (hash, &wq->hash->map)) {__set_current_state (TASK_RUNNING );list_del_init (&wqe->wait.entry); + ret = true; } }spin_unlock_irq (&wq->hash->wait.lock); +return ret; } 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, }if (stall_hash !=-1U ) { +bool unstalled; +/* * Set this before dropping the lock to avoid racing with new * work being added and clearing the stalled bit. */ set_bit (IO_ACCT_STALLED_BIT , &acct->flags);raw_spin_unlock (&wqe->lock); -io_wait_on_hash (wqe, stall_hash); + unstalled =io_wait_on_hash (wqe, stall_hash);raw_spin_lock (&wqe->lock); +if (unstalled) { +clear_bit (IO_ACCT_STALLED_BIT , &acct->flags); +if (wq_has_sleeper (&wqe->wq->hash->wait)) +wake_up (&wqe->wq->hash->wait); + } }return NULL ;
@@ -564,8 +574,11 @@ static void io_worker_handle_work(struct io_worker *worker)io_wqe_enqueue (wqe, linked);if (hash !=-1U && !next_hashed) { +/* serialize hash clear with wake_up() */ +spin_lock_irq (&wq->hash->wait.lock);clear_bit (hash, &wq->hash->map);clear_bit (IO_ACCT_STALLED_BIT , &acct->flags); +spin_unlock_irq (&wq->hash->wait.lock);if (wq_has_sleeper (&wq->hash->wait))wake_up (&wq->hash->wait);raw_spin_lock (&wqe->lock);
Quoted 5 lines · On 11/14/21 7:25 AM, [email protected] wrote:
The patch below does not apply to the 5.14-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
This needs a few more backports - here are two backports that it depends on, and then this one backported as well.
Signature · 1 line
Jens Axboe
From 32d9a0f2e60679746c1fbb9488b59273c641a1ad Mon Sep 17 00:00:00 2001
From: Jens Axboe <[email protected]>
Date: Sun, 14 Nov 2021 07:36:47 -0700
Subject: [PATCH 3/3] io-wq: serialize hash clear with wakeup
commit d3e3c102d107bb84251455a298cf475f24bab995 upstream.
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.
--- fs/io-wq.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
fs/io-wq.c +16 -3
@@ -401,9 +401,10 @@ static inline unsigned int io_get_work_hash(struct io_wq_work *work)return work->flags >>IO_WQ_HASH_SHIFT ; } -static void io_wait_on_hash (struct io_wqe *wqe,unsigned int hash) +static bool io_wait_on_hash (struct io_wqe *wqe,unsigned int hash) {struct io_wq *wq = wqe->wq; +bool ret = false;spin_lock_irq (&wq->hash->wait.lock);if (list_empty (&wqe->wait.entry)) {
@@ -411,9 +412,11 @@ static void io_wait_on_hash(struct io_wqe *wqe, unsigned int hash)if (!test_bit (hash, &wq->hash->map)) {__set_current_state (TASK_RUNNING );list_del_init (&wqe->wait.entry); + ret = true; } }spin_unlock_irq (&wq->hash->wait.lock); +return ret; }/*
@@ -474,14 +477,21 @@ static struct io_wq_work *io_get_next_work(struct io_wqe *wqe, }if (stall_hash !=-1U ) { +bool unstalled; +/* * Set this before dropping the lock to avoid racing with new * work being added and clearing the stalled bit. */ wqe->flags |=IO_WQE_FLAG_STALLED ;raw_spin_unlock (&wqe->lock); -io_wait_on_hash (wqe, stall_hash); + unstalled =io_wait_on_hash (wqe, stall_hash);raw_spin_lock (&wqe->lock); +if (unstalled) { + wqe->flags &= ~IO_WQE_FLAG_STALLED ; +if (wq_has_sleeper (&wqe->wq->hash->wait)) +wake_up (&wqe->wq->hash->wait); + } }return NULL ;
@@ -562,11 +572,14 @@ static void io_worker_handle_work(struct io_worker *worker)io_wqe_enqueue (wqe, linked);if (hash !=-1U && !next_hashed) { +/* serialize hash clear with wake_up() */ +spin_lock_irq (&wq->hash->wait.lock);clear_bit (hash, &wq->hash->map); + wqe->flags &= ~IO_WQE_FLAG_STALLED ; +spin_unlock_irq (&wq->hash->wait.lock);if (wq_has_sleeper (&wq->hash->wait))wake_up (&wq->hash->wait);raw_spin_lock_irq (&wqe->lock); - wqe->flags &= ~IO_WQE_FLAG_STALLED ;/* skip unnecessary unlock-lock wqe->lock */ if (!work) goto get_next;
Signature · 1 line
2.33.1
From bd0e0402c3cc5299703ac59ddc04f795b9fbcbd0 Mon Sep 17 00:00:00 2001
From: Jens Axboe <[email protected]>
Date: Tue, 31 Aug 2021 06:57:25 -0600
Subject: [PATCH 1/3] io-wq: ensure that hash wait lock is IRQ disabling
commit 08bdbd39b58474d762242e1fadb7f2eb9ffcca71 upstream.
A previous commit removed the IRQ safety of the worker and wqe locks, but that left one spot of the hash wait lock now being done without already having IRQs disabled.
Ensure that we use the right locking variant for the hashed waitqueue lock.
--- fs/io-wq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/io-wq.c +2 -2
@@ -405,7 +405,7 @@ static void io_wait_on_hash(struct io_wqe *wqe, unsigned int hash) {struct io_wq *wq = wqe->wq; -spin_lock (&wq->hash->wait.lock); +spin_lock_irq (&wq->hash->wait.lock);if (list_empty (&wqe->wait.entry)) {__add_wait_queue (&wq->hash->wait, &wqe->wait);if (!test_bit (hash, &wq->hash->map)) {
@@ -413,7 +413,7 @@ static void io_wait_on_hash(struct io_wqe *wqe, unsigned int hash)list_del_init (&wqe->wait.entry); } } -spin_unlock (&wq->hash->wait.lock); +spin_unlock_irq (&wq->hash->wait.lock); }/*
Signature · 1 line
2.33.1
From 955c228ba82f86c5c20fd9c56403f4abb0d7e384 Mon Sep 17 00:00:00 2001
From: Jens Axboe <[email protected]>
Date: Tue, 31 Aug 2021 13:53:00 -0600
Subject: [PATCH 2/3] io-wq: fix queue stalling race
commit 0242f6426ea78fbe3933b44f8c55ae93ec37f6cc upstream.
We need to set the stalled bit early, before we drop the lock for adding us to the stall hash queue. If not, then we can race with new work being queued between adding us to the stall hash and io_worker_handle_work() marking us stalled.
--- fs/io-wq.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-)
fs/io-wq.c +7 -8
@@ -436,8 +436,7 @@ static bool io_worker_can_run_work(struct io_worker *worker, }static struct io_wq_work *io_get_next_work (struct io_wqe *wqe, -struct io_worker *worker, -bool *stalled) +struct io_worker *worker) __must_hold(wqe->lock) {struct io_wq_work_node *node, *prev;
@@ -475,10 +474,14 @@ static struct io_wq_work *io_get_next_work(struct io_wqe *wqe, }if (stall_hash !=-1U ) { +/* +* Set this before dropping the lock to avoid racing with new +* work being added and clearing the stalled bit. +*/ + wqe->flags |=IO_WQE_FLAG_STALLED ;raw_spin_unlock (&wqe->lock);io_wait_on_hash (wqe, stall_hash);raw_spin_lock (&wqe->lock); - *stalled = true; }return NULL ;
@@ -518,7 +521,6 @@ static void io_worker_handle_work(struct io_worker *worker) do { struct io_wq_work *work; - bool stalled; get_next: /* * If we got some work, mark us as busy. If we didn't, but
@@ -527,12 +529,9 @@ static void io_worker_handle_work(struct io_worker *worker)* can't make progress, any work completion or insertion will * clear the stalled flag. */ - stalled = false; - work =io_get_next_work (wqe, worker, &stalled); + work =io_get_next_work (wqe, worker);if (work)__io_worker_busy (wqe, worker, work); -else if (stalled) - wqe->flags |=IO_WQE_FLAG_STALLED ;raw_spin_unlock_irq (&wqe->lock);if (!work)
Signature · 1 line
2.33.1
Greg KH
maintainerQuoted 9 lines · Jens Axboe
Quoted 5 lines · On 11/14/21 7:25 AM, [email protected] wrote:
The patch below does not apply to the 5.14-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.This needs a few more backports - here are two backports that it depends on, and then this one backported as well.
Now queued up, thanks.
greg k-h