Jan Kara 2ee3a6
From: Jan Kara <jack@suse.cz>
Jan Kara 2ee3a6
Subject: [PATCH] sbitmap: Avoid lockups when waker gets preempted
Jan Kara 2ee3a6
References: bsc#1209118
Jan Kara 2ee3a6
Patch-mainline: Never, sbitmap code got rewritten
Jan Kara 2ee3a6
Jan Kara 2ee3a6
When the process performing wakeup on the wait queue gets preempted and this
Jan Kara 2ee3a6
wait queue has the only waiter, we can loose many wakeups and in the end we
Jan Kara 2ee3a6
may end up with waiter in the wait queue without anybody able to wake him up.
Jan Kara 2ee3a6
Jan Kara 2ee3a6
The scenario can by like: We have waitqueue 0 active.
Jan Kara 2ee3a6
Jan Kara 2ee3a6
  CPU0                              CPU1
Jan Kara 2ee3a6
  __sbq_wake_up()
Jan Kara 2ee3a6
    decrements wait_cnt to 0
Jan Kara 2ee3a6
    wake_up_nr() -> wakes waiters
Jan Kara 2ee3a6
                                    queues IOs until all tags are used
Jan Kara 2ee3a6
                                    blk_mq_get_tag() finds no tag ->
Jan Kara 2ee3a6
                                      adds waiter to waitqueue 0
Jan Kara 2ee3a6
                                    all IOs complete - wakeups discarded because 
Jan Kara 2ee3a6
                                      waitqueue 0 still has wait_cnt == 0.
Jan Kara 2ee3a6
   set wait_cnt to 8
Jan Kara 2ee3a6
Jan Kara 2ee3a6
And waiter in waitqueue 0 sleeps indefinitely now because there's no IO to
Jan Kara 2ee3a6
complete to wake him up.
Jan Kara 2ee3a6
Jan Kara 2ee3a6
Fix the problem by waking up some waiters if we don't find any valid wait queue
Jan Kara 2ee3a6
to perform wake up on. This makes sure we don't loose all the wake ups.
Jan Kara 2ee3a6
Jan Kara 2ee3a6
Signed-off-by: Jan Kara <jack@suse.cz>
Jan Kara 2ee3a6
Jan Kara 2ee3a6
---
Jan Kara 2ee3a6
 lib/sbitmap.c |   22 ++++++++++++++++++----
Jan Kara 2ee3a6
 1 file changed, 18 insertions(+), 4 deletions(-)
Jan Kara 2ee3a6
Jan Kara 2ee3a6
--- a/lib/sbitmap.c
Jan Kara 2ee3a6
+++ b/lib/sbitmap.c
Jan Kara 2ee3a6
@@ -532,6 +532,7 @@ EXPORT_SYMBOL_GPL(sbitmap_queue_min_shal
Jan Kara 2ee3a6
 static struct sbq_wait_state *sbq_wake_ptr(struct sbitmap_queue *sbq)
Jan Kara 2ee3a6
 {
Jan Kara 2ee3a6
 	int i, wake_index;
Jan Kara 2ee3a6
+	struct sbq_wait_state *active_ws = NULL;
Jan Kara 2ee3a6
 
Jan Kara 2ee3a6
 	if (!atomic_read(&sbq->ws_active))
Jan Kara 2ee3a6
 		return NULL;
Jan Kara 2ee3a6
@@ -540,15 +541,28 @@ static struct sbq_wait_state *sbq_wake_p
Jan Kara 2ee3a6
 	for (i = 0; i < SBQ_WAIT_QUEUES; i++) {
Jan Kara 2ee3a6
 		struct sbq_wait_state *ws = &sbq->ws[wake_index];
Jan Kara 2ee3a6
 
Jan Kara 2ee3a6
-		if (waitqueue_active(&ws->wait) && atomic_read(&ws->wait_cnt) > 0) {
Jan Kara 2ee3a6
-			if (wake_index != atomic_read(&sbq->wake_index))
Jan Kara 2ee3a6
-				atomic_set(&sbq->wake_index, wake_index);
Jan Kara 2ee3a6
-			return ws;
Jan Kara 2ee3a6
+		if (waitqueue_active(&ws->wait)) {
Jan Kara 2ee3a6
+			if (atomic_read(&ws->wait_cnt) > 0) {
Jan Kara 2ee3a6
+				if (wake_index != atomic_read(&sbq->wake_index))
Jan Kara 2ee3a6
+					atomic_set(&sbq->wake_index, wake_index);
Jan Kara 2ee3a6
+				return ws;
Jan Kara 2ee3a6
+			}
Jan Kara 2ee3a6
+			active_ws = ws;
Jan Kara 2ee3a6
 		}
Jan Kara 2ee3a6
 
Jan Kara 2ee3a6
 		wake_index = sbq_index_inc(wake_index);
Jan Kara 2ee3a6
 	}
Jan Kara 2ee3a6
 
Jan Kara 2ee3a6
+	/*
Jan Kara 2ee3a6
+	 * There are active waitqueues but all are in the process of being
Jan Kara 2ee3a6
+	 * woken. Perform wakeup on some waitqueue to avoid loosing the wakeup.
Jan Kara 2ee3a6
+	 * This is actually important in case task performing wakeup gets
Jan Kara 2ee3a6
+	 * preempted and lots of other wakeup events happen before it gets
Jan Kara 2ee3a6
+	 * scheduled again.
Jan Kara 2ee3a6
+	 */
Jan Kara 2ee3a6
+	if (active_ws)
Jan Kara 2ee3a6
+		wake_up_nr(&active_ws->wait, READ_ONCE(sbq->wake_batch));
Jan Kara 2ee3a6
+
Jan Kara 2ee3a6
 	return NULL;
Jan Kara 2ee3a6
 }
Jan Kara 2ee3a6