]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
null_blk: do partial IO for bad blocks
authorShin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Wed, 26 Feb 2025 10:06:13 +0000 (19:06 +0900)
committerJens Axboe <axboe@kernel.dk>
Mon, 3 Mar 2025 18:17:52 +0000 (11:17 -0700)
The current null_blk implementation checks if any bad blocks exist in
the target blocks of each IO. If so, the IO fails and data is not
transferred for all of the IO target blocks. However, when real storage
devices have bad blocks, the devices may transfer data partially up to
the first bad blocks (e.g., SAS drives). Especially, when the IO is a
write operation, such partial IO leaves partially written data on the
device.

To simulate such partial IO using null_blk, introduce the new parameter
'badblocks_partial_io'. When this parameter is set,
null_handle_badblocks() returns the number of the sectors for the
partial IO as its third pointer argument. Pass the returned number of
sectors to the following calls to null_handle_memory_backend() in
null_process_cmd() and null_zone_write().

Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Link: https://lore.kernel.org/r/20250226100613.1622564-6-shinichiro.kawasaki@wdc.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
drivers/block/null_blk/main.c
drivers/block/null_blk/null_blk.h
drivers/block/null_blk/zoned.c

index 8025766988124b8711d4b64c0ac7b2d5d3cb9711..31d44cef68414585a3f38eb0c2bf47e00975042d 100644 (file)
@@ -474,6 +474,7 @@ NULLB_DEVICE_ATTR(shared_tag_bitmap, bool, NULL);
 NULLB_DEVICE_ATTR(fua, bool, NULL);
 NULLB_DEVICE_ATTR(rotational, bool, NULL);
 NULLB_DEVICE_ATTR(badblocks_once, bool, NULL);
+NULLB_DEVICE_ATTR(badblocks_partial_io, bool, NULL);
 
 static ssize_t nullb_device_power_show(struct config_item *item, char *page)
 {
@@ -595,6 +596,7 @@ CONFIGFS_ATTR_WO(nullb_device_, zone_offline);
 static struct configfs_attribute *nullb_device_attrs[] = {
        &nullb_device_attr_badblocks,
        &nullb_device_attr_badblocks_once,
+       &nullb_device_attr_badblocks_partial_io,
        &nullb_device_attr_blocking,
        &nullb_device_attr_blocksize,
        &nullb_device_attr_cache_size,
@@ -1321,19 +1323,40 @@ static inline blk_status_t null_handle_throttled(struct nullb_cmd *cmd)
        return sts;
 }
 
+/*
+ * Check if the command should fail for the badblocks. If so, return
+ * BLK_STS_IOERR and return number of partial I/O sectors to be written or read,
+ * which may be less than the requested number of sectors.
+ *
+ * @cmd:        The command to handle.
+ * @sector:     The start sector for I/O.
+ * @nr_sectors: Specifies number of sectors to write or read, and returns the
+ *              number of sectors to be written or read.
+ */
 blk_status_t null_handle_badblocks(struct nullb_cmd *cmd, sector_t sector,
-                                  sector_t nr_sectors)
+                                  unsigned int *nr_sectors)
 {
        struct badblocks *bb = &cmd->nq->dev->badblocks;
+       struct nullb_device *dev = cmd->nq->dev;
+       unsigned int block_sectors = dev->blocksize >> SECTOR_SHIFT;
        sector_t first_bad;
        int bad_sectors;
+       unsigned int partial_io_sectors = 0;
 
-       if (!badblocks_check(bb, sector, nr_sectors, &first_bad, &bad_sectors))
+       if (!badblocks_check(bb, sector, *nr_sectors, &first_bad, &bad_sectors))
                return BLK_STS_OK;
 
        if (cmd->nq->dev->badblocks_once)
                badblocks_clear(bb, first_bad, bad_sectors);
 
+       if (cmd->nq->dev->badblocks_partial_io) {
+               if (!IS_ALIGNED(first_bad, block_sectors))
+                       first_bad = ALIGN_DOWN(first_bad, block_sectors);
+               if (sector < first_bad)
+                       partial_io_sectors = first_bad - sector;
+       }
+       *nr_sectors = partial_io_sectors;
+
        return BLK_STS_IOERR;
 }
 
@@ -1392,18 +1415,19 @@ blk_status_t null_process_cmd(struct nullb_cmd *cmd, enum req_op op,
                              sector_t sector, unsigned int nr_sectors)
 {
        struct nullb_device *dev = cmd->nq->dev;
+       blk_status_t badblocks_ret = BLK_STS_OK;
        blk_status_t ret;
 
-       if (dev->badblocks.shift != -1) {
-               ret = null_handle_badblocks(cmd, sector, nr_sectors);
+       if (dev->badblocks.shift != -1)
+               badblocks_ret = null_handle_badblocks(cmd, sector, &nr_sectors);
+
+       if (dev->memory_backed && nr_sectors) {
+               ret = null_handle_memory_backed(cmd, op, sector, nr_sectors);
                if (ret != BLK_STS_OK)
                        return ret;
        }
 
-       if (dev->memory_backed)
-               return null_handle_memory_backed(cmd, op, sector, nr_sectors);
-
-       return BLK_STS_OK;
+       return badblocks_ret;
 }
 
 static void null_handle_cmd(struct nullb_cmd *cmd, sector_t sector,
index ee60f3a8879671d62a42cbdd72dfc88e72aba35a..7bb6128dbaafb706bca1859dac33e8991590d670 100644 (file)
@@ -64,6 +64,7 @@ struct nullb_device {
        unsigned int curr_cache;
        struct badblocks badblocks;
        bool badblocks_once;
+       bool badblocks_partial_io;
 
        unsigned int nr_zones;
        unsigned int nr_zones_imp_open;
@@ -133,11 +134,10 @@ blk_status_t null_handle_discard(struct nullb_device *dev, sector_t sector,
 blk_status_t null_process_cmd(struct nullb_cmd *cmd, enum req_op op,
                              sector_t sector, unsigned int nr_sectors);
 blk_status_t null_handle_badblocks(struct nullb_cmd *cmd, sector_t sector,
-                                  sector_t nr_sectors);
+                                  unsigned int *nr_sectors);
 blk_status_t null_handle_memory_backed(struct nullb_cmd *cmd, enum req_op op,
                                       sector_t sector, sector_t nr_sectors);
 
-
 #ifdef CONFIG_BLK_DEV_ZONED
 int null_init_zoned_dev(struct nullb_device *dev, struct queue_limits *lim);
 int null_register_zoned_dev(struct nullb *nullb);
index 7677f6cf23f4665d4453ca57ccf74c4e9c3f4a16..4e5728f459899fb4c91228da2773b5378f996dd8 100644 (file)
@@ -353,6 +353,7 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
        struct nullb_device *dev = cmd->nq->dev;
        unsigned int zno = null_zone_no(dev, sector);
        struct nullb_zone *zone = &dev->zones[zno];
+       blk_status_t badblocks_ret = BLK_STS_OK;
        blk_status_t ret;
 
        trace_nullb_zone_op(cmd, zno, zone->cond);
@@ -413,9 +414,11 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
        }
 
        if (dev->badblocks.shift != -1) {
-               ret = null_handle_badblocks(cmd, sector, nr_sectors);
-               if (ret != BLK_STS_OK)
+               badblocks_ret = null_handle_badblocks(cmd, sector, &nr_sectors);
+               if (badblocks_ret != BLK_STS_OK && !nr_sectors) {
+                       ret = badblocks_ret;
                        goto unlock_zone;
+               }
        }
 
        if (dev->memory_backed) {
@@ -438,7 +441,7 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
                zone->cond = BLK_ZONE_COND_FULL;
        }
 
-       ret = BLK_STS_OK;
+       ret = badblocks_ret;
 
 unlock_zone:
        null_unlock_zone(dev, zone);