Hannes Reinecke 57427b
From ebd780ac233884d6ad298e29ba13d002da2ee5be Mon Sep 17 00:00:00 2001
Hannes Reinecke 57427b
From: Hannes Reinecke <hare@suse.de>
Hannes Reinecke 57427b
Date: Fri, 14 May 2021 12:44:38 +0200
Hannes Reinecke 57427b
Subject: [PATCH] block/genhd: use atomic_t for disk_event->block
Hannes Reinecke 57427b
References: bsc#1185497
Hannes Reinecke 57427b
Patch-Mainline: submitted linux-block 2021/06/01
Hannes Reinecke 57427b
Hannes Reinecke 57427b
__disk_unblock_events() will call queue_delayed_work() with a '0' argument
Hannes Reinecke 57427b
under a spin lock. This might cause the queue_work item to be executed
Hannes Reinecke 57427b
immediately, and run into a deadlock in disk_check_events() waiting for
Hannes Reinecke 57427b
the lock to be released.
Hannes Reinecke 57427b
Hannes Reinecke 57427b
This patch converts the 'blocked' counter into an atomic variable, so we don't
Hannes Reinecke 57427b
need to hold a spinlock anymore when scheduling the workqueue function.
Hannes Reinecke 57427b
Hannes Reinecke 57427b
Signed-off-by: Hannes Reinecke <hare@suse.de>
Hannes Reinecke 57427b
---
Hannes Reinecke 57427b
 block/genhd.c | 36 +++++++++++++-----------------------
Hannes Reinecke 57427b
 1 file changed, 13 insertions(+), 23 deletions(-)
Hannes Reinecke 57427b
Hannes Reinecke 57427b
diff --git a/block/genhd.c b/block/genhd.c
Hannes Reinecke 57427b
index ab29d1a723b2..e1c3b9d2b5d6 100644
Hannes Reinecke 57427b
--- a/block/genhd.c
Hannes Reinecke 57427b
+++ b/block/genhd.c
Hannes Reinecke 57427b
@@ -1731,7 +1731,7 @@ struct disk_events {
Hannes Reinecke 57427b
 	spinlock_t		lock;
Hannes Reinecke 57427b
 
Hannes Reinecke 57427b
 	struct mutex		block_mutex;	/* protects blocking */
Hannes Reinecke 57427b
-	int			block;		/* event blocking depth */
Hannes Reinecke 57427b
+	atomic_t		block;		/* event blocking depth */
Hannes Reinecke 57427b
 	unsigned int		pending;	/* events already sent out */
Hannes Reinecke 57427b
 	unsigned int		clearing;	/* events being cleared */
Hannes Reinecke 57427b
 
Hannes Reinecke 57427b
@@ -1791,8 +1791,6 @@ static unsigned long disk_events_poll_jiffies(struct gendisk *disk)
Hannes Reinecke 57427b
 void disk_block_events(struct gendisk *disk)
Hannes Reinecke 57427b
 {
Hannes Reinecke 57427b
 	struct disk_events *ev = disk->ev;
Hannes Reinecke 57427b
-	unsigned long flags;
Hannes Reinecke 57427b
-	bool cancel;
Hannes Reinecke 57427b
 
Hannes Reinecke 57427b
 	if (!ev)
Hannes Reinecke 57427b
 		return;
Hannes Reinecke 57427b
@@ -1803,11 +1801,7 @@ void disk_block_events(struct gendisk *disk)
Hannes Reinecke 57427b
 	 */
Hannes Reinecke 57427b
 	mutex_lock(&ev->block_mutex);
Hannes Reinecke 57427b
 
Hannes Reinecke 57427b
-	spin_lock_irqsave(&ev->lock, flags);
Hannes Reinecke 57427b
-	cancel = !ev->block++;
Hannes Reinecke 57427b
-	spin_unlock_irqrestore(&ev->lock, flags);
Hannes Reinecke 57427b
-
Hannes Reinecke 57427b
-	if (cancel)
Hannes Reinecke 57427b
+	if (atomic_inc_return(&ev->block) == 1)
Hannes Reinecke 57427b
 		cancel_delayed_work_sync(&disk->ev->dwork);
Hannes Reinecke 57427b
 
Hannes Reinecke 57427b
 	mutex_unlock(&ev->block_mutex);
Hannes Reinecke 57427b
@@ -1819,23 +1813,19 @@ static void __disk_unblock_events(struct gendisk *disk, bool check_now)
Hannes Reinecke 57427b
 	unsigned long intv;
Hannes Reinecke 57427b
 	unsigned long flags;
Hannes Reinecke 57427b
 
Hannes Reinecke 57427b
+	if (atomic_dec_return(&ev->block) <= 0) {
Hannes Reinecke 57427b
+		mutex_unlock(&ev->block_mutex);
Hannes Reinecke 57427b
+		return;
Hannes Reinecke 57427b
+	}
Hannes Reinecke 57427b
 	spin_lock_irqsave(&ev->lock, flags);
Hannes Reinecke 57427b
-
Hannes Reinecke 57427b
-	if (WARN_ON_ONCE(ev->block <= 0))
Hannes Reinecke 57427b
-		goto out_unlock;
Hannes Reinecke 57427b
-
Hannes Reinecke 57427b
-	if (--ev->block)
Hannes Reinecke 57427b
-		goto out_unlock;
Hannes Reinecke 57427b
-
Hannes Reinecke 57427b
 	intv = disk_events_poll_jiffies(disk);
Hannes Reinecke 57427b
+	spin_unlock_irqrestore(&ev->lock, flags);
Hannes Reinecke 57427b
 	if (check_now)
Hannes Reinecke 57427b
 		queue_delayed_work(system_freezable_power_efficient_wq,
Hannes Reinecke 57427b
 				&ev->dwork, 0);
Hannes Reinecke 57427b
 	else if (intv)
Hannes Reinecke 57427b
 		queue_delayed_work(system_freezable_power_efficient_wq,
Hannes Reinecke 57427b
 				&ev->dwork, intv);
Hannes Reinecke 57427b
-out_unlock:
Hannes Reinecke 57427b
-	spin_unlock_irqrestore(&ev->lock, flags);
Hannes Reinecke 57427b
 }
Hannes Reinecke 57427b
 
Hannes Reinecke 57427b
 /**
Hannes Reinecke 57427b
@@ -1875,10 +1865,10 @@ void disk_flush_events(struct gendisk *disk, unsigned int mask)
Hannes Reinecke 57427b
 
Hannes Reinecke 57427b
 	spin_lock_irq(&ev->lock);
Hannes Reinecke 57427b
 	ev->clearing |= mask;
Hannes Reinecke 57427b
-	if (!ev->block)
Hannes Reinecke 57427b
+	spin_unlock_irq(&ev->lock);
Hannes Reinecke 57427b
+	if (!atomic_read(&ev->block))
Hannes Reinecke 57427b
 		mod_delayed_work(system_freezable_power_efficient_wq,
Hannes Reinecke 57427b
 				&ev->dwork, 0);
Hannes Reinecke 57427b
-	spin_unlock_irq(&ev->lock);
Hannes Reinecke 57427b
 }
Hannes Reinecke 57427b
 
Hannes Reinecke 57427b
 /**
Hannes Reinecke 57427b
@@ -1969,11 +1959,11 @@ static void disk_check_events(struct disk_events *ev,
Hannes Reinecke 57427b
 	*clearing_ptr &= ~clearing;
Hannes Reinecke 57427b
 
Hannes Reinecke 57427b
 	intv = disk_events_poll_jiffies(disk);
Hannes Reinecke 57427b
-	if (!ev->block && intv)
Hannes Reinecke 57427b
+	spin_unlock_irq(&ev->lock);
Hannes Reinecke 57427b
+	if (!atomic_read(&ev->block) && intv)
Hannes Reinecke 57427b
 		queue_delayed_work(system_freezable_power_efficient_wq,
Hannes Reinecke 57427b
 				&ev->dwork, intv);
Hannes Reinecke 57427b
 
Hannes Reinecke 57427b
-	spin_unlock_irq(&ev->lock);
Hannes Reinecke 57427b
 
Hannes Reinecke 57427b
 	/*
Hannes Reinecke 57427b
 	 * Tell userland about new events.  Only the events listed in
Hannes Reinecke 57427b
@@ -2138,7 +2128,7 @@ static void disk_alloc_events(struct gendisk *disk)
Hannes Reinecke 57427b
 	ev->disk = disk;
Hannes Reinecke 57427b
 	spin_lock_init(&ev->lock);
Hannes Reinecke 57427b
 	mutex_init(&ev->block_mutex);
Hannes Reinecke 57427b
-	ev->block = 1;
Hannes Reinecke 57427b
+	atomic_set(&ev->block, 1);
Hannes Reinecke 57427b
 	ev->poll_msecs = -1;
Hannes Reinecke 57427b
 	INIT_DELAYED_WORK(&ev->dwork, disk_events_workfn);
Hannes Reinecke 57427b
 
Hannes Reinecke 57427b
@@ -2182,6 +2172,6 @@ static void disk_del_events(struct gendisk *disk)
Hannes Reinecke 57427b
 static void disk_release_events(struct gendisk *disk)
Hannes Reinecke 57427b
 {
Hannes Reinecke 57427b
 	/* the block count should be 1 from disk_del_events() */
Hannes Reinecke 57427b
-	WARN_ON_ONCE(disk->ev && disk->ev->block != 1);
Hannes Reinecke 57427b
+	WARN_ON_ONCE(disk->ev && atomic_read(&disk->ev->block) != 1);
Hannes Reinecke 57427b
 	kfree(disk->ev);
Hannes Reinecke 57427b
 }
Hannes Reinecke 57427b
-- 
Hannes Reinecke 57427b
2.29.2
Hannes Reinecke 57427b