blob: 06d1e9412decf5bd591311882ebe15ca57451773 [file] [log] [blame]
From 4299c95d39434d687181d4c7ed3db8000b8d441b Mon Sep 17 00:00:00 2001
From: David Jeffery <>
Date: Tue, 17 Dec 2019 11:00:24 -0500
Subject: [PATCH] sbitmap: only queue kyber's wait callback if not already
commit df034c93f15ee71df231ff9fe311d27ff08a2a52 upstream.
Under heavy loads where the kyber I/O scheduler hits the token limits for
its scheduling domains, kyber can become stuck. When active requests
complete, kyber may not be woken up leaving the I/O requests in kyber
This stuck state is due to a race condition with kyber and the sbitmap
functions it uses to run a callback when enough requests have completed.
The running of a sbt_wait callback can race with the attempt to insert the
sbt_wait. Since sbitmap_del_wait_queue removes the sbt_wait from the list
first then sets the sbq field to NULL, kyber can see the item as not on a
list but the call to sbitmap_add_wait_queue will see sbq as non-NULL. This
results in the sbt_wait being inserted onto the wait list but ws_active
doesn't get incremented. So the sbitmap queue does not know there is a
waiter on a wait list.
Since sbitmap doesn't think there is a waiter, kyber may never be
informed that there are domain tokens available and the I/O never advances.
With the sbt_wait on a wait list, kyber believes it has an active waiter
so cannot insert a new waiter when reaching the domain's full state.
This race can be fixed by only adding the sbt_wait to the queue if the
sbq field is NULL. If sbq is not NULL, there is already an action active
which will trigger the re-running of kyber. Let it run and add the
sbt_wait to the wait list if still needing to wait.
Reviewed-by: Omar Sandoval <>
Signed-off-by: David Jeffery <>
Reported-by: John Pittman <>
Tested-by: John Pittman <>
Signed-off-by: Jens Axboe <>
Signed-off-by: Paul Gortmaker <>
diff --git a/lib/sbitmap.c b/lib/sbitmap.c
index 54f57cd117c6..85ab49344a23 100644
--- a/lib/sbitmap.c
+++ b/lib/sbitmap.c
@@ -671,8 +671,8 @@ void sbitmap_add_wait_queue(struct sbitmap_queue *sbq,
if (!sbq_wait->sbq) {
sbq_wait->sbq = sbq;
+ add_wait_queue(&ws->wait, &sbq_wait->wait);
- add_wait_queue(&ws->wait, &sbq_wait->wait);