]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
zloop: introduce the zone_append configuration parameter
authorDamien Le Moal <dlemoal@kernel.org>
Sat, 15 Nov 2025 12:15:54 +0000 (21:15 +0900)
committerJens Axboe <axboe@kernel.dk>
Mon, 17 Nov 2025 16:40:09 +0000 (09:40 -0700)
A zloop zoned block device declares to the block layer that it supports
zone append operations. That is, a zloop device ressembles an NVMe ZNS
devices supporting zone append.

This native support is fine but it does not allow exercising the block
layer zone write plugging emulation of zone append, as is done with SCSI
or ATA SMR HDDs.

Introduce the zone_append configuration parameter to allow creating a
zloop device without native support for zone append, thus relying on the
block layer zone append emulation. If not specified, zone append support
is enabled by default. Otherwise, a value of 0 disables native zone
append and a value of 1 enables it.

Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
drivers/block/zloop.c

index 0526277f6cd17046d214e6ee8053f189b36c7259..cf9be42ca3e1f9343c8842d170bd27ab8155bdd1 100644 (file)
@@ -32,6 +32,7 @@ enum {
        ZLOOP_OPT_NR_QUEUES             = (1 << 6),
        ZLOOP_OPT_QUEUE_DEPTH           = (1 << 7),
        ZLOOP_OPT_BUFFERED_IO           = (1 << 8),
+       ZLOOP_OPT_ZONE_APPEND           = (1 << 9),
 };
 
 static const match_table_t zloop_opt_tokens = {
@@ -44,6 +45,7 @@ static const match_table_t zloop_opt_tokens = {
        { ZLOOP_OPT_NR_QUEUES,          "nr_queues=%u"          },
        { ZLOOP_OPT_QUEUE_DEPTH,        "queue_depth=%u"        },
        { ZLOOP_OPT_BUFFERED_IO,        "buffered_io"           },
+       { ZLOOP_OPT_ZONE_APPEND,        "zone_append=%u"        },
        { ZLOOP_OPT_ERR,                NULL                    }
 };
 
@@ -56,6 +58,7 @@ static const match_table_t zloop_opt_tokens = {
 #define ZLOOP_DEF_NR_QUEUES            1
 #define ZLOOP_DEF_QUEUE_DEPTH          128
 #define ZLOOP_DEF_BUFFERED_IO          false
+#define ZLOOP_DEF_ZONE_APPEND          true
 
 /* Arbitrary limit on the zone size (16GB). */
 #define ZLOOP_MAX_ZONE_SIZE_MB         16384
@@ -71,6 +74,7 @@ struct zloop_options {
        unsigned int            nr_queues;
        unsigned int            queue_depth;
        bool                    buffered_io;
+       bool                    zone_append;
 };
 
 /*
@@ -108,6 +112,7 @@ struct zloop_device {
 
        struct workqueue_struct *workqueue;
        bool                    buffered_io;
+       bool                    zone_append;
 
        const char              *base_dir;
        struct file             *data_dir;
@@ -378,6 +383,11 @@ static void zloop_rw(struct zloop_cmd *cmd)
        cmd->nr_sectors = nr_sectors;
        cmd->ret = 0;
 
+       if (WARN_ON_ONCE(is_append && !zlo->zone_append)) {
+               ret = -EIO;
+               goto out;
+       }
+
        /* We should never get an I/O beyond the device capacity. */
        if (WARN_ON_ONCE(zone_no >= zlo->nr_zones)) {
                ret = -EIO;
@@ -889,7 +899,6 @@ static int zloop_ctl_add(struct zloop_options *opts)
 {
        struct queue_limits lim = {
                .max_hw_sectors         = SZ_1M >> SECTOR_SHIFT,
-               .max_hw_zone_append_sectors = SZ_1M >> SECTOR_SHIFT,
                .chunk_sectors          = opts->zone_size,
                .features               = BLK_FEAT_ZONED,
        };
@@ -941,6 +950,7 @@ static int zloop_ctl_add(struct zloop_options *opts)
        zlo->nr_zones = nr_zones;
        zlo->nr_conv_zones = opts->nr_conv_zones;
        zlo->buffered_io = opts->buffered_io;
+       zlo->zone_append = opts->zone_append;
 
        zlo->workqueue = alloc_workqueue("zloop%d", WQ_UNBOUND | WQ_FREEZABLE,
                                opts->nr_queues * opts->queue_depth, zlo->id);
@@ -981,6 +991,8 @@ static int zloop_ctl_add(struct zloop_options *opts)
 
        lim.physical_block_size = zlo->block_size;
        lim.logical_block_size = zlo->block_size;
+       if (zlo->zone_append)
+               lim.max_hw_zone_append_sectors = lim.max_hw_sectors;
 
        zlo->tag_set.ops = &zloop_mq_ops;
        zlo->tag_set.nr_hw_queues = opts->nr_queues;
@@ -1021,10 +1033,13 @@ static int zloop_ctl_add(struct zloop_options *opts)
        zlo->state = Zlo_live;
        mutex_unlock(&zloop_ctl_mutex);
 
-       pr_info("Added device %d: %u zones of %llu MB, %u B block size\n",
+       pr_info("zloop: device %d, %u zones of %llu MiB, %u B block size\n",
                zlo->id, zlo->nr_zones,
                ((sector_t)zlo->zone_size << SECTOR_SHIFT) >> 20,
                zlo->block_size);
+       pr_info("zloop%d: using %s zone append\n",
+               zlo->id,
+               zlo->zone_append ? "native" : "emulated");
 
        return 0;
 
@@ -1111,6 +1126,7 @@ static int zloop_parse_options(struct zloop_options *opts, const char *buf)
        opts->nr_queues = ZLOOP_DEF_NR_QUEUES;
        opts->queue_depth = ZLOOP_DEF_QUEUE_DEPTH;
        opts->buffered_io = ZLOOP_DEF_BUFFERED_IO;
+       opts->zone_append = ZLOOP_DEF_ZONE_APPEND;
 
        if (!buf)
                return 0;
@@ -1220,6 +1236,18 @@ static int zloop_parse_options(struct zloop_options *opts, const char *buf)
                case ZLOOP_OPT_BUFFERED_IO:
                        opts->buffered_io = true;
                        break;
+               case ZLOOP_OPT_ZONE_APPEND:
+                       if (match_uint(args, &token)) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
+                       if (token != 0 && token != 1) {
+                               pr_err("Invalid zone_append value\n");
+                               ret = -EINVAL;
+                               goto out;
+                       }
+                       opts->zone_append = token;
+                       break;
                case ZLOOP_OPT_ERR:
                default:
                        pr_warn("unknown parameter or missing value '%s'\n", p);