[PATCH v2 06/12] null_blk: Enable lock context analysis

Bart Van Assche <[email protected]> Thu, 30 Jul 2026 12:58:29 -0700
Newsgroups org.kernel.vger.linux-block
Message-ID <55886b395224068987cec253192ae6257e61c960.1785440858.git.bvanassche@acm.org>
Add __must_hold() annotations where these are missing. Because
null_lock_zone() and null_unlock_zone() use conditional locking, instead
of annotating these functions, introduce guard class null_zone. Replace
null_lock_zone() and null_unlock_zone() calls with scoped_guard(null_zone,
...). Enable lock context analysis in the Makefile.

Signed-off-by: Bart Van Assche <[email protected]>
---
 drivers/block/null_blk/Makefile |   2 +
 drivers/block/null_blk/main.c   |   4 ++
 drivers/block/null_blk/zoned.c  | 120 ++++++++++++++------------------
 3 files changed, 57 insertions(+), 69 deletions(-)

diff --git a/drivers/block/null_blk/Makefile b/drivers/block/null_blk/Makefile
index 84c36e512ab8..282b0d51a477 100644
--- a/drivers/block/null_blk/Makefile
+++ b/drivers/block/null_blk/Makefile
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 
+CONTEXT_ANALYSIS := y
+
 # needed for trace events
 ccflags-y			+= -I$(src)
 
diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
index f8c0fd57e041..baffc6c61178 100644
--- a/drivers/block/null_blk/main.c
+++ b/drivers/block/null_blk/main.c
@@ -1038,6 +1038,7 @@ static struct nullb_page *null_insert_page(struct nullb *nullb,
 }
 
 static int null_flush_cache_page(struct nullb *nullb, struct nullb_page *c_page)
+	__must_hold(&nullb->lock)
 {
 	int i;
 	unsigned int offset;
@@ -1087,6 +1088,7 @@ static int null_flush_cache_page(struct nullb *nullb, struct nullb_page *c_page)
 }
 
 static int null_make_cache_space(struct nullb *nullb, unsigned long n)
+	__must_hold(&nullb->lock)
 {
 	int i, err, nr_pages;
 	struct nullb_page *c_pages[FREE_BATCH];
@@ -1141,6 +1143,7 @@ static int null_make_cache_space(struct nullb *nullb, unsigned long n)
 
 static blk_status_t copy_to_nullb(struct nullb *nullb, void *source,
 				  loff_t pos, size_t n, bool is_fua)
+	__must_hold(&nullb->lock)
 {
 	size_t temp, count = 0;
 	struct nullb_page *t_page;
@@ -1242,6 +1245,7 @@ static blk_status_t null_handle_flush(struct nullb *nullb)
 static blk_status_t null_transfer(struct nullb *nullb, struct page *page,
 	unsigned int len, unsigned int off, bool is_write, loff_t pos,
 	bool is_fua)
+	__must_hold(&nullb->lock)
 {
 	struct nullb_device *dev = nullb->dev;
 	blk_status_t err = BLK_STS_OK;
diff --git a/drivers/block/null_blk/zoned.c b/drivers/block/null_blk/zoned.c
index 384bdce6a9b7..dbae748c90ba 100644
--- a/drivers/block/null_blk/zoned.c
+++ b/drivers/block/null_blk/zoned.c
@@ -30,23 +30,26 @@ static inline void null_init_zone_lock(struct nullb_device *dev,
 		mutex_init(&zone->mutex);
 }
 
-static inline void null_lock_zone(struct nullb_device *dev,
-				  struct nullb_zone *zone)
-{
-	if (!dev->memory_backed)
-		spin_lock_irq(&zone->spinlock);
-	else
-		mutex_lock(&zone->mutex);
-}
-
-static inline void null_unlock_zone(struct nullb_device *dev,
-				    struct nullb_zone *zone)
-{
-	if (!dev->memory_backed)
-		spin_unlock_irq(&zone->spinlock);
-	else
-		mutex_unlock(&zone->mutex);
-}
+struct nullb_dev_and_zone {
+	struct nullb_device *dev;
+	struct nullb_zone *zone;
+};
+
+DEFINE_CLASS(null_zone, struct nullb_dev_and_zone, ({
+		     if (!_T.dev->memory_backed)
+			     spin_unlock_irq(&_T.zone->spinlock);
+		     else
+			     mutex_unlock(&_T.zone->mutex);
+	     }), ({
+		     if (!dev->memory_backed)
+			     spin_lock_irq(&zone->spinlock);
+		     else
+			     mutex_lock(&zone->mutex);
+		     (struct nullb_dev_and_zone){ dev, zone };
+	     }),
+	     struct nullb_device *dev, struct nullb_zone *zone)
+
+DEFINE_CLASS_IS_UNCONDITIONAL(null_zone)
 
 int null_init_zoned_dev(struct nullb_device *dev,
 			struct queue_limits *lim)
@@ -216,14 +219,14 @@ int null_report_zones(struct gendisk *disk, sector_t sector,
 		 * So use a local copy to avoid corruption of the device zone
 		 * array.
 		 */
-		null_lock_zone(dev, zone);
-		blkz.start = zone->start;
-		blkz.len = zone->len;
-		blkz.wp = zone->wp;
-		blkz.type = zone->type;
-		blkz.cond = zone->cond;
-		blkz.capacity = zone->capacity;
-		null_unlock_zone(dev, zone);
+		scoped_guard(null_zone, dev, zone) {
+			blkz.start = zone->start;
+			blkz.len = zone->len;
+			blkz.wp = zone->wp;
+			blkz.type = zone->type;
+			blkz.cond = zone->cond;
+			blkz.capacity = zone->capacity;
+		}
 
 		error = disk_report_zone(disk, &blkz, i, args);
 		if (error)
@@ -364,7 +367,7 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
 		return null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors);
 	}
 
-	null_lock_zone(dev, zone);
+	guard(null_zone)(dev, zone);
 
 	/*
 	 * Regular writes must be at the write pointer position. Zone append
@@ -376,19 +379,15 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
 	 */
 	if (append) {
 		if (WARN_ON_ONCE(!dev->zone_append_max_sectors) ||
-		    zone->wp == NULL_ZONE_INVALID_WP) {
-			ret = BLK_STS_IOERR;
-			goto unlock_zone;
-		}
+		    zone->wp == NULL_ZONE_INVALID_WP)
+			return BLK_STS_IOERR;
 		sector = zone->wp;
 		blk_mq_rq_from_pdu(cmd)->__sector = sector;
 	}
 
 	if (sector != zone->wp ||
-	    zone->wp + nr_sectors > zone->start + zone->capacity) {
-		ret = BLK_STS_IOERR;
-		goto unlock_zone;
-	}
+	    zone->wp + nr_sectors > zone->start + zone->capacity)
+		return BLK_STS_IOERR;
 
 	if (zone->cond == BLK_ZONE_COND_CLOSED ||
 	    zone->cond == BLK_ZONE_COND_EMPTY) {
@@ -398,7 +397,7 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
 			ret = null_check_zone_resources(dev, zone);
 			if (ret != BLK_STS_OK) {
 				spin_unlock(&dev->zone_res_lock);
-				goto unlock_zone;
+				return ret;
 			}
 			if (zone->cond == BLK_ZONE_COND_CLOSED) {
 				dev->nr_zones_closed--;
@@ -415,17 +414,15 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
 
 	if (dev->badblocks.shift != -1) {
 		badblocks_ret = null_handle_badblocks(cmd, sector, &nr_sectors);
-		if (badblocks_ret != BLK_STS_OK && !nr_sectors) {
-			ret = badblocks_ret;
-			goto unlock_zone;
-		}
+		if (badblocks_ret != BLK_STS_OK && !nr_sectors)
+			return badblocks_ret;
 	}
 
 	if (dev->memory_backed) {
 		ret = null_handle_memory_backed(cmd, REQ_OP_WRITE, sector,
 						nr_sectors);
 		if (ret != BLK_STS_OK)
-			goto unlock_zone;
+			return ret;
 	}
 
 	zone->wp += nr_sectors;
@@ -441,12 +438,7 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
 		zone->cond = BLK_ZONE_COND_FULL;
 	}
 
-	ret = badblocks_ret;
-
-unlock_zone:
-	null_unlock_zone(dev, zone);
-
-	return ret;
+	return badblocks_ret;
 }
 
 static blk_status_t null_open_zone(struct nullb_device *dev,
@@ -655,14 +647,14 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_op op,
 	if (op == REQ_OP_ZONE_RESET_ALL) {
 		for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) {
 			zone = &dev->zones[i];
-			null_lock_zone(dev, zone);
-			if (zone->cond != BLK_ZONE_COND_EMPTY &&
-			    zone->cond != BLK_ZONE_COND_READONLY &&
-			    zone->cond != BLK_ZONE_COND_OFFLINE) {
-				null_reset_zone(dev, zone);
-				trace_nullb_zone_op(cmd, i, zone->cond);
+			scoped_guard(null_zone, dev, zone) {
+				if (zone->cond != BLK_ZONE_COND_EMPTY &&
+				    zone->cond != BLK_ZONE_COND_READONLY &&
+				    zone->cond != BLK_ZONE_COND_OFFLINE) {
+					null_reset_zone(dev, zone);
+					trace_nullb_zone_op(cmd, i, zone->cond);
+				}
 			}
-			null_unlock_zone(dev, zone);
 		}
 		return BLK_STS_OK;
 	}
@@ -670,13 +662,11 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_op op,
 	zone_no = null_zone_no(dev, sector);
 	zone = &dev->zones[zone_no];
 
-	null_lock_zone(dev, zone);
+	guard(null_zone)(dev, zone);
 
 	if (zone->cond == BLK_ZONE_COND_READONLY ||
-	    zone->cond == BLK_ZONE_COND_OFFLINE) {
-		ret = BLK_STS_IOERR;
-		goto unlock;
-	}
+	    zone->cond == BLK_ZONE_COND_OFFLINE)
+		return BLK_STS_IOERR;
 
 	switch (op) {
 	case REQ_OP_ZONE_RESET:
@@ -699,9 +689,6 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_op op,
 	if (ret == BLK_STS_OK)
 		trace_nullb_zone_op(cmd, zone_no, zone->cond);
 
-unlock:
-	null_unlock_zone(dev, zone);
-
 	return ret;
 }
 
@@ -710,7 +697,6 @@ blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_op op,
 {
 	struct nullb_device *dev;
 	struct nullb_zone *zone;
-	blk_status_t sts;
 
 	switch (op) {
 	case REQ_OP_WRITE:
@@ -729,10 +715,8 @@ blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_op op,
 		if (zone->cond == BLK_ZONE_COND_OFFLINE)
 			return BLK_STS_IOERR;
 
-		null_lock_zone(dev, zone);
-		sts = null_process_cmd(cmd, op, sector, nr_sectors);
-		null_unlock_zone(dev, zone);
-		return sts;
+		scoped_guard(null_zone, dev, zone)
+			return null_process_cmd(cmd, op, sector, nr_sectors);
 	}
 }
 
@@ -746,7 +730,7 @@ static void null_set_zone_cond(struct nullb_device *dev,
 			 cond != BLK_ZONE_COND_OFFLINE))
 		return;
 
-	null_lock_zone(dev, zone);
+	guard(null_zone)(dev, zone);
 
 	/*
 	 * If the read-only condition is requested again to zones already in
@@ -767,8 +751,6 @@ static void null_set_zone_cond(struct nullb_device *dev,
 		zone->cond = cond;
 		zone->wp = NULL_ZONE_INVALID_WP;
 	}
-
-	null_unlock_zone(dev, zone);
 }
 
 /*