From e4db6c1c1e08d50bfa203961e196f3e42b28c330 Mon Sep 17 00:00:00 2001
From: Joseph Qi <joseph.qi@linux.alibaba.com>
Date: Sat, 12 Jun 2021 09:08:49 +0800
Subject: [PATCH 2924/2944] alinux: use spin_lock_irqsave instead in
 ioc_rqos_throttle()

fix #34995459

We encounter a hard lockup when running iocost testcase:

[ 2409.519288] RIP: 0010:ioc_rqos_throttle+0x2d5/0x850
[ 2409.519289] Code: 00 00 48 89 84 24 98 00 00 00 48 89 5d 00 48 8b 74 24 10 4c 89 ff e8 2a c5 ff ff 41 c6 87 90 00 00 00 00 fb 66 0f 1f 44 00 00 <65> 48 8b 1c 25 c0 5b 01 00 48 c7 44 24 58 02 00 00 00 48 8b 44 24
[ 2409.519289] RSP: 0018:ffffbccf68e27670 EFLAGS: 00000286 ORIG_RAX: ffffffffffffffde
[ 2409.519290] RAX: ffff9eb63f9dd3c0 RBX: ffffbccf68e27708 RCX: 00000000000006e0
[ 2409.519290] RDX: 00000000000af5df RSI: 0000000000000083 RDI: 0000000000000083
[ 2409.519290] RBP: ffff9eb634e15298 R08: 000002230f932079 R09: ffffbccf68e276f0
[ 2409.519291] R10: 0000000000315c6c R11: 000000000000ebb6 R12: ffff9e962cabe524
[ 2409.519291] R13: 0000000000324822 R14: ffff9eb634e15260 R15: ffff9eb634e15200
[ 2409.519291]  rq_qos_throttle+0x31/0x40
[ 2409.519292]  blk_queue_bio+0xb0/0x3d0
[ 2409.519292]  generic_make_request+0x16f/0x350
[ 2409.519293]  submit_bio+0x9e/0x170
[ 2409.519293]  submit_bh_wbc+0x157/0x190
[ 2409.519294]  ll_rw_block+0x6b/0xb0
[ 2409.519294]  __breadahead+0x30/0x50
[ 2409.519294]  __ext4_get_inode_loc+0x3dd/0x490
[ 2409.519294]  __ext4_iget+0x104/0xd80
[ 2409.519295]  ext4_lookup+0xb7/0x1c0
[ 2409.519296]  __lookup_slow+0x80/0x130
[ 2409.519296]  lookup_slow+0x35/0x50
[ 2409.519296]  lookup_one_len_unlocked+0x40/0x50
[ 2409.519296]  lookup_positive_unlocked+0xa/0x30
[ 2409.519297]  ovl_lookup_single+0x2c/0x260 [overlay]
[ 2409.519297]  ovl_lookup_layer+0x115/0x170 [overlay]
[ 2409.519297]  ovl_lookup+0x213/0xa10 [overlay]
[ 2409.519298]  __lookup_slow+0x80/0x130
[ 2409.519299]  lookup_slow+0x35/0x50
[ 2409.519299]  walk_component+0x15e/0x280
[ 2409.519299]  link_path_walk+0x181/0x500
[ 2409.519300]  path_openat+0x95/0x240
[ 2409.519300]  do_filp_open+0x7e/0xd0
[ 2409.519302]  do_sys_openat2+0x1b6/0x260
[ 2409.519302]  do_sys_open+0x4a/0x50
[ 2409.519302]  do_syscall_64+0x5b/0x1b0
[ 2409.519303]  entry_SYSCALL_64_after_hwframe+0x44/0xa9

This is because it locks q->queue_lock with spin_lock_irq in
blk_queue_bio(), and then locks/unlocks iocg->waitq.lock with
spin_lock_irq/spin_unlock_irq in rq_qos_throttle(). So use
spin_lock_irqsave/spin_unlock_irqrestore primitives instead.

Tested-by: Jeffle Xu <jefflexu@linux.alibaba.com>
Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Acked-by: Xiaoguang Wang <xiaoguang.wang@linux.alibaba.com>
---
 block/blk-iocost.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/block/blk-iocost.c b/block/blk-iocost.c
index 896eb31..3d2b9f7 100644
--- a/block/blk-iocost.c
+++ b/block/blk-iocost.c
@@ -1028,6 +1028,7 @@ static bool iocg_activate(struct ioc_gq *iocg, struct ioc_now *now)
 	u64 last_period, cur_period, max_period_delta;
 	u64 vtime, vmargin, vmin;
 	int i;
+	unsigned long flags;
 
 	/*
 	 * If seem to be already active, just update the stamp to tell the
@@ -1045,7 +1046,7 @@ static bool iocg_activate(struct ioc_gq *iocg, struct ioc_now *now)
 	if (iocg->child_active_sum)
 		return false;
 
-	spin_lock_irq(&ioc->lock);
+	spin_lock_irqsave(&ioc->lock, flags);
 
 	ioc_now(ioc, now);
 
@@ -1103,11 +1104,11 @@ static bool iocg_activate(struct ioc_gq *iocg, struct ioc_now *now)
 	}
 
 succeed_unlock:
-	spin_unlock_irq(&ioc->lock);
+	spin_unlock_irqrestore(&ioc->lock, flags);
 	return true;
 
 fail_unlock:
-	spin_unlock_irq(&ioc->lock);
+	spin_unlock_irqrestore(&ioc->lock, flags);
 	return false;
 }
 
@@ -1732,6 +1733,7 @@ static void ioc_rqos_throttle(struct rq_qos *rqos, struct bio *bio, spinlock_t *
 	struct iocg_wait wait;
 	u32 hw_active, hw_inuse;
 	u64 abs_cost, cost, vtime;
+	unsigned long flags;
 
 	/* bypass IOs if disabled */
 	if (!ioc->enabled)
@@ -1781,9 +1783,9 @@ static void ioc_rqos_throttle(struct rq_qos *rqos, struct bio *bio, spinlock_t *
 	    time_after_eq64(vtime + ioc->inuse_margin_vtime, now.vnow)) {
 		TRACE_IOCG_PATH(inuse_reset, iocg, &now,
 				iocg->inuse, iocg->weight, hw_inuse, hw_active);
-		spin_lock_irq(&ioc->lock);
+		spin_lock_irqsave(&ioc->lock, flags);
 		propagate_active_weight(iocg, iocg->weight, iocg->weight);
-		spin_unlock_irq(&ioc->lock);
+		spin_unlock_irqrestore(&ioc->lock, flags);
 		current_hweight(iocg, &hw_active, &hw_inuse);
 	}
 
@@ -1806,10 +1808,10 @@ static void ioc_rqos_throttle(struct rq_qos *rqos, struct bio *bio, spinlock_t *
 	 * as we're waiting or has debt, so we're good if we're activated
 	 * here. In the unlikely case that we aren't, just issue the IO.
 	 */
-	spin_lock_irq(&iocg->waitq.lock);
+	spin_lock_irqsave(&iocg->waitq.lock, flags);
 
 	if (unlikely(list_empty(&iocg->active_list))) {
-		spin_unlock_irq(&iocg->waitq.lock);
+		spin_unlock_irqrestore(&iocg->waitq.lock, flags);
 		iocg_commit_bio(iocg, bio, cost);
 		return;
 	}
@@ -1836,7 +1838,7 @@ static void ioc_rqos_throttle(struct rq_qos *rqos, struct bio *bio, spinlock_t *
 		if (iocg_kick_delay(iocg, &now))
 			blkcg_schedule_throttle(rqos->q,
 					(bio->bi_opf & REQ_SWAP) == REQ_SWAP);
-		spin_unlock_irq(&iocg->waitq.lock);
+		spin_unlock_irqrestore(&iocg->waitq.lock, flags);
 		return;
 	}
 
@@ -1862,7 +1864,7 @@ static void ioc_rqos_throttle(struct rq_qos *rqos, struct bio *bio, spinlock_t *
 	__add_wait_queue_entry_tail(&iocg->waitq, &wait.wait);
 	iocg_kick_waitq(iocg, &now);
 
-	spin_unlock_irq(&iocg->waitq.lock);
+	spin_unlock_irqrestore(&iocg->waitq.lock, flags);
 
 	while (true) {
 		set_current_state(TASK_UNINTERRUPTIBLE);
-- 
1.8.3.1

