| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/06 19:46 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"BLK_DEV_ZONED",
"BLK_DEV_NULL_BLK",
"BLK_DEV_ZONED_LOOP"
],
"FocusSymbols": [
"bdev_check_zone_mgmt",
"blkdev_get_zone_info",
"blk_revalidate_disk_zones",
"disk_release_zone_resources",
"disk_init_zone_resources"
],
"Reasoning": "The patch refactors how zone conditions are tracked in the block layer, introducing `enum blk_zstate` and a new `zones_state` array to replace `zones_cond`. It updates zone management checks (`bdev_check_zone_mgmt`), zone revalidation (`blk_revalidate_disk_zones`), and zone information retrieval (`blkdev_get_zone_info`). It also modifies `__add_disk` to disable partitions on zoned block devices. These are functional changes to the core block layer's zoned storage support and should be fuzzed to ensure no regressions in zone state tracking or bio submission.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/06 19:46 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit c0528e68e9702e1ffdef1d23f21f27247d7e674a\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Aug 6 19:46:22 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/block/blk-core.c b/block/blk-core.c\nindex 196bccf27f58d..80a4e2bcc3f34 100644\n--- a/block/blk-core.c\n+++ b/block/blk-core.c\n@@ -901,8 +901,11 @@ void submit_bio_noacct(struct bio *bio)\n \tcase REQ_OP_ZONE_CLOSE:\n \tcase REQ_OP_ZONE_RESET:\n \tcase REQ_OP_ZONE_FINISH:\n-\t\t/* Zone management operations require sequential zones. */\n-\t\tif (!bdev_zone_is_seq(bio-\u003ebi_bdev, bio-\u003ebi_iter.bi_sector))\n+\t\t/*\n+\t\t * Zone management operations require sequential zones that are\n+\t\t * not offline nor read-only.\n+\t\t */\n+\t\tif (!bdev_check_zone_mgmt(bdev, bio-\u003ebi_iter.bi_sector))\n \t\t\tgoto end_io;\n \t\tbreak;\n \tcase REQ_OP_ZONE_RESET_ALL:\ndiff --git a/block/blk-zoned.c b/block/blk-zoned.c\nindex a5afb842bf35e..f17069309ac9d 100644\n--- a/block/blk-zoned.c\n+++ b/block/blk-zoned.c\n@@ -39,6 +39,103 @@ static const char *const zone_cond_name[] = {\n };\n #undef ZONE_COND_NAME\n \n+/*\n+ * Internal and compact representation of enum blk_zone_cond values for zone\n+ * conditions. All these values fit into 4-bits, allowing using the high order\n+ * bits as the zone type.\n+ */\n+enum blk_zstate {\n+\tBLK_ZSTATE_NOT_WP\t= 0x00,\n+\tBLK_ZSTATE_EMPTY\t= 0x01,\n+\tBLK_ZSTATE_IMP_OPEN\t= 0x02,\n+\tBLK_ZSTATE_EXP_OPEN\t= 0x03,\n+\tBLK_ZSTATE_CLOSED\t= 0x04,\n+\tBLK_ZSTATE_READONLY\t= 0x05,\n+\tBLK_ZSTATE_FULL\t\t= 0x06,\n+\tBLK_ZSTATE_OFFLINE\t= 0x07,\n+\tBLK_ZSTATE_ACTIVE\t= 0x08,\n+\n+\tBLK_ZSTATE_COND_MASK\t= 0x0F,\n+\n+\t/* Conventional zone. */\n+\tBLK_ZFLAG_CONV\t\t= 0x80,\n+\tBLK_ZSTATE_FLAGS_MASK\t= ~BLK_ZSTATE_COND_MASK,\n+};\n+\n+/*\n+ * Lookup table and helper to convert enum blk_zstate conditions into enum\n+ * blk_zone_condition values.\n+ */\n+static const u8 blk_zstate2zcond[] = {\n+\t[BLK_ZSTATE_NOT_WP]\t= BLK_ZONE_COND_NOT_WP,\n+\t[BLK_ZSTATE_EMPTY]\t= BLK_ZONE_COND_EMPTY,\n+\t[BLK_ZSTATE_IMP_OPEN]\t= BLK_ZONE_COND_IMP_OPEN,\n+\t[BLK_ZSTATE_EXP_OPEN]\t= BLK_ZONE_COND_EXP_OPEN,\n+\t[BLK_ZSTATE_CLOSED]\t= BLK_ZONE_COND_CLOSED,\n+\t[BLK_ZSTATE_READONLY]\t= BLK_ZONE_COND_READONLY,\n+\t[BLK_ZSTATE_FULL]\t= BLK_ZONE_COND_FULL,\n+\t[BLK_ZSTATE_OFFLINE]\t= BLK_ZONE_COND_OFFLINE,\n+\t[BLK_ZSTATE_ACTIVE]\t= BLK_ZONE_COND_ACTIVE,\n+};\n+\n+static inline enum blk_zone_cond blk_zstate_to_zone_cond(enum blk_zstate zs)\n+{\n+\tu8 idx = zs \u0026 BLK_ZSTATE_COND_MASK;\n+\n+\tif (WARN_ON_ONCE(idx \u003e= ARRAY_SIZE(blk_zstate2zcond)))\n+\t\treturn 0;\n+\n+\treturn blk_zstate2zcond[idx];\n+}\n+\n+/*\n+ * Lookup table and helper to convert an enum blk_zone_condition into an enum\n+ * blk_zstate condition value. To keep the lookup table small, the\n+ * BLK_ZONE_COND_ACTIVE condition is not added and handled separately.\n+ */\n+static const u8 blk_zcond2zstate[] = {\n+\t[BLK_ZONE_COND_NOT_WP]\t\t= BLK_ZSTATE_NOT_WP,\n+\t[BLK_ZONE_COND_EMPTY]\t\t= BLK_ZSTATE_EMPTY,\n+\t[BLK_ZONE_COND_IMP_OPEN]\t= BLK_ZSTATE_ACTIVE,\n+\t[BLK_ZONE_COND_EXP_OPEN]\t= BLK_ZSTATE_ACTIVE,\n+\t[BLK_ZONE_COND_CLOSED]\t\t= BLK_ZSTATE_ACTIVE,\n+\t[BLK_ZONE_COND_READONLY]\t= BLK_ZSTATE_READONLY,\n+\t[BLK_ZONE_COND_FULL]\t\t= BLK_ZSTATE_FULL,\n+\t[BLK_ZONE_COND_OFFLINE]\t\t= BLK_ZSTATE_OFFLINE,\n+};\n+\n+static inline enum blk_zstate blk_zone_cond_to_zstate(enum blk_zone_cond cond)\n+{\n+\tif (cond == BLK_ZONE_COND_ACTIVE)\n+\t\treturn BLK_ZSTATE_ACTIVE;\n+\n+\tif (WARN_ON_ONCE(cond \u003e= ARRAY_SIZE(blk_zcond2zstate)))\n+\t\treturn 0;\n+\n+\treturn blk_zcond2zstate[cond];\n+}\n+\n+/*\n+ * Combine an enum blk_zone_condition and zone flags into a zones_state array\n+ * entry.\n+ */\n+static inline void blk_zstate_set(u8 *zones_state, unsigned int zno,\n+\t\t\t\t enum blk_zone_cond cond, u8 flags)\n+{\n+\tif (zones_state)\n+\t\tzones_state[zno] = flags | blk_zone_cond_to_zstate(cond);\n+}\n+\n+static inline u8 blk_zstate_flags(enum blk_zstate zs)\n+{\n+\treturn zs \u0026 BLK_ZSTATE_FLAGS_MASK;\n+}\n+\n+static inline bool blk_zstate_is_conv(enum blk_zstate zs)\n+{\n+\treturn blk_zstate_flags(zs) \u0026 BLK_ZFLAG_CONV;\n+}\n+\n /*\n * Per-zone write plug.\n * @node: hlist_node structure for managing the plug using a hash table.\n@@ -135,55 +232,64 @@ const char *blk_zone_cond_str(enum blk_zone_cond zone_cond)\n }\n EXPORT_SYMBOL_GPL(blk_zone_cond_str);\n \n-static void blk_zone_set_cond(u8 *zones_cond, unsigned int zno,\n-\t\t\t enum blk_zone_cond cond)\n+static void disk_zone_set_cond(struct gendisk *disk, sector_t sector,\n+\t\t\t enum blk_zone_cond cond)\n {\n-\tif (!zones_cond)\n-\t\treturn;\n+\tunsigned int zno = disk_zone_no(disk, sector);\n+\tu8 *zones_state;\n \n-\tswitch (cond) {\n-\tcase BLK_ZONE_COND_IMP_OPEN:\n-\tcase BLK_ZONE_COND_EXP_OPEN:\n-\tcase BLK_ZONE_COND_CLOSED:\n-\t\tzones_cond[zno] = BLK_ZONE_COND_ACTIVE;\n-\t\treturn;\n-\tcase BLK_ZONE_COND_NOT_WP:\n-\tcase BLK_ZONE_COND_EMPTY:\n-\tcase BLK_ZONE_COND_FULL:\n-\tcase BLK_ZONE_COND_OFFLINE:\n-\tcase BLK_ZONE_COND_READONLY:\n-\tdefault:\n-\t\tzones_cond[zno] = cond;\n-\t\treturn;\n-\t}\n+\trcu_read_lock();\n+\tzones_state = rcu_dereference(disk-\u003ezones_state);\n+\tif (likely(zones_state \u0026\u0026 zno \u003c disk-\u003enr_zones))\n+\t\tblk_zstate_set(zones_state, zno, cond,\n+\t\t\t blk_zstate_flags(zones_state[zno]));\n+\trcu_read_unlock();\n }\n \n-static void disk_zone_set_cond(struct gendisk *disk, sector_t sector,\n-\t\t\t enum blk_zone_cond cond)\n+static inline u8 disk_zone_get_state(struct gendisk *disk, sector_t sector)\n {\n-\tu8 *zones_cond;\n+\tunsigned int zno = disk_zone_no(disk, sector);\n+\tu8 *zones_state, zs;\n \n \trcu_read_lock();\n-\tzones_cond = rcu_dereference(disk-\u003ezones_cond);\n-\tif (zones_cond) {\n-\t\tunsigned int zno = disk_zone_no(disk, sector);\n-\n-\t\t/*\n-\t\t * The condition of a conventional, readonly and offline zones\n-\t\t * never changes, so do nothing if the target zone is in one of\n-\t\t * these conditions.\n-\t\t */\n-\t\tswitch (zones_cond[zno]) {\n-\t\tcase BLK_ZONE_COND_NOT_WP:\n-\t\tcase BLK_ZONE_COND_READONLY:\n-\t\tcase BLK_ZONE_COND_OFFLINE:\n-\t\t\tbreak;\n-\t\tdefault:\n-\t\t\tblk_zone_set_cond(zones_cond, zno, cond);\n-\t\t\tbreak;\n-\t\t}\n-\t}\n+\tzones_state = rcu_dereference(disk-\u003ezones_state);\n+\tif (likely(zones_state \u0026\u0026 zno \u003c disk-\u003enr_zones))\n+\t\tzs = zones_state[zno];\n+\telse\n+\t\tzs = 0;\n \trcu_read_unlock();\n+\n+\treturn zs;\n+}\n+\n+static enum blk_zone_cond disk_zone_get_cond(struct gendisk *disk,\n+\t\t\t\t\t sector_t sector)\n+{\n+\tu8 zs = disk_zone_get_state(disk, sector);\n+\n+\treturn blk_zstate_to_zone_cond(zs);\n+}\n+\n+static inline bool\n+disk_zone_cond_is_offline_or_readonly(enum blk_zone_cond cond)\n+{\n+\treturn cond == BLK_ZONE_COND_READONLY ||\n+\t\tcond == BLK_ZONE_COND_OFFLINE;\n+}\n+\n+static inline bool disk_zone_is_offline_or_readonly(struct gendisk *disk,\n+\t\t\t\t\t\t sector_t sector)\n+{\n+\tenum blk_zone_cond cond = disk_zone_get_cond(disk, sector);\n+\n+\treturn disk_zone_cond_is_offline_or_readonly(cond);\n+}\n+\n+static bool disk_zone_is_seq(struct gendisk *disk, sector_t sector)\n+{\n+\tu8 zs = disk_zone_get_state(disk, sector);\n+\n+\treturn !blk_zstate_is_conv(zs);\n }\n \n /**\n@@ -195,23 +301,36 @@ static void disk_zone_set_cond(struct gendisk *disk, sector_t sector,\n */\n bool bdev_zone_is_seq(struct block_device *bdev, sector_t sector)\n {\n-\tstruct gendisk *disk = bdev-\u003ebd_disk;\n-\tunsigned int zno = disk_zone_no(disk, sector);\n-\tbool is_seq = false;\n-\tu8 *zones_cond;\n+\tif (!bdev_is_zoned(bdev))\n+\t\treturn false;\n+\n+\treturn disk_zone_is_seq(bdev-\u003ebd_disk, sector);\n+}\n+EXPORT_SYMBOL_GPL(bdev_zone_is_seq);\n+\n+/**\n+ * bdev_check_zone_mgmt - check if a sector belongs to a valid sequential zone\n+ * @bdev: block device to check\n+ * @sector: sector number\n+ *\n+ * Check if @sector on @bdev is contained in a sequential write required zone\n+ * that is not offline nor read-only.\n+ */\n+bool bdev_check_zone_mgmt(struct block_device *bdev, sector_t sector)\n+{\n+\tenum blk_zone_cond cond;\n+\tu8 zs;\n \n \tif (!bdev_is_zoned(bdev))\n \t\treturn false;\n \n-\trcu_read_lock();\n-\tzones_cond = rcu_dereference(disk-\u003ezones_cond);\n-\tif (zones_cond \u0026\u0026 zno \u003c disk-\u003enr_zones)\n-\t\tis_seq = zones_cond[zno] != BLK_ZONE_COND_NOT_WP;\n-\trcu_read_unlock();\n+\tzs = disk_zone_get_state(bdev-\u003ebd_disk, sector);\n+\tif (blk_zstate_is_conv(zs))\n+\t\treturn false;\n \n-\treturn is_seq;\n+\tcond = blk_zstate_to_zone_cond(zs);\n+\treturn !disk_zone_cond_is_offline_or_readonly(cond);\n }\n-EXPORT_SYMBOL_GPL(bdev_zone_is_seq);\n \n /*\n * Zone report arguments for block device drivers report_zones operation.\n@@ -500,12 +619,17 @@ static bool disk_zone_wplug_is_full(struct gendisk *disk,\n \treturn zwplug-\u003ewp_offset \u003e= disk-\u003elast_zone_capacity;\n }\n \n+static bool disk_zone_wplug_is_offline_or_readonly(struct blk_zone_wplug *zwplug)\n+{\n+\treturn disk_zone_cond_is_offline_or_readonly(zwplug-\u003econd);\n+}\n+\n static bool disk_insert_zone_wplug(struct gendisk *disk,\n \t\t\t\t struct blk_zone_wplug *zwplug)\n {\n \tstruct blk_zone_wplug *zwplg;\n \tunsigned long flags;\n-\tu8 *zones_cond;\n+\tu8 *zones_state;\n \tunsigned int idx =\n \t\thash_32(zwplug-\u003ezone_no, disk-\u003ezone_wplugs_hash_bits);\n \n@@ -524,15 +648,16 @@ static bool disk_insert_zone_wplug(struct gendisk *disk,\n \t}\n \n \t/*\n-\t * Set the zone condition: if we do not yet have a zones_cond array\n+\t * Set the zone condition: if we do not yet have a zones_state array\n \t * attached to the disk, then this is a zone write plug insert from the\n \t * first call to blk_revalidate_disk_zones(), in which case the zone is\n \t * necessarilly in the active condition.\n \t */\n-\tzones_cond = rcu_dereference_check(disk-\u003ezones_cond,\n+\tzones_state = rcu_dereference_check(disk-\u003ezones_state,\n \t\t\t\tlockdep_is_held(\u0026disk-\u003ezone_wplugs_hash_lock));\n-\tif (zones_cond)\n-\t\tzwplug-\u003econd = zones_cond[zwplug-\u003ezone_no];\n+\tif (zones_state)\n+\t\tzwplug-\u003econd =\n+\t\t\tblk_zstate_to_zone_cond(zones_state[zwplug-\u003ezone_no]);\n \telse\n \t\tzwplug-\u003econd = BLK_ZONE_COND_ACTIVE;\n \n@@ -574,6 +699,26 @@ static inline struct blk_zone_wplug *disk_get_zone_wplug(struct gendisk *disk,\n \treturn disk_get_hashed_zone_wplug(disk, sector);\n }\n \n+static void disk_for_all_zone_wplugs(struct gendisk *disk,\n+\t\t\t\t void (*actor)(struct blk_zone_wplug *,\n+\t\t\t\t\t\t void *),\n+\t\t\t\t void *data)\n+{\n+\tstruct blk_zone_wplug *zwplug;\n+\tunsigned int i;\n+\n+\tif (!disk-\u003ezone_wplugs_hash)\n+\t\treturn;\n+\n+\trcu_read_lock();\n+\tfor (i = 0; i \u003c disk_zone_wplugs_hash_size(disk); i++) {\n+\t\thlist_for_each_entry_rcu(zwplug, \u0026disk-\u003ezone_wplugs_hash[i],\n+\t\t\t\t\t node)\n+\t\t\tactor(zwplug, data);\n+\t}\n+\trcu_read_unlock();\n+}\n+\n static void disk_free_zone_wplug_rcu(struct rcu_head *rcu_head)\n {\n \tstruct blk_zone_wplug *zwplug =\n@@ -592,9 +737,9 @@ static void disk_free_zone_wplug(struct blk_zone_wplug *zwplug)\n \tWARN_ON_ONCE(!bio_list_empty(\u0026zwplug-\u003ebio_list));\n \n \tspin_lock_irqsave(\u0026disk-\u003ezone_wplugs_hash_lock, flags);\n-\tblk_zone_set_cond(rcu_dereference_check(disk-\u003ezones_cond,\n+\tblk_zstate_set(rcu_dereference_check(disk-\u003ezones_state,\n \t\t\t\tlockdep_is_held(\u0026disk-\u003ezone_wplugs_hash_lock)),\n-\t\t\t zwplug-\u003ezone_no, zwplug-\u003econd);\n+\t\t zwplug-\u003ezone_no, zwplug-\u003econd, 0);\n \thlist_del_init_rcu(\u0026zwplug-\u003enode);\n \tatomic_dec(\u0026disk-\u003enr_zone_wplugs);\n \tspin_unlock_irqrestore(\u0026disk-\u003ezone_wplugs_hash_lock, flags);\n@@ -608,6 +753,53 @@ static inline void disk_put_zone_wplug(struct blk_zone_wplug *zwplug)\n \t\tdisk_free_zone_wplug(zwplug);\n }\n \n+static inline void blk_zone_wplug_bio_io_error(struct blk_zone_wplug *zwplug,\n+\t\t\t\t\t struct bio *bio)\n+{\n+\tstruct request_queue *q = zwplug-\u003edisk-\u003equeue;\n+\n+\tbio_clear_flag(bio, BIO_ZONE_WRITE_PLUGGING);\n+\tbio_io_error(bio);\n+\tdisk_put_zone_wplug(zwplug);\n+\t/* Drop the reference taken by disk_zone_wplug_add_bio(). */\n+\tblk_queue_exit(q);\n+}\n+\n+/*\n+ * Abort (fail) all plugged BIOs of a zone write plug.\n+ */\n+static void disk_zone_wplug_abort(struct blk_zone_wplug *zwplug)\n+{\n+\tstruct gendisk *disk = zwplug-\u003edisk;\n+\tstruct bio *bio;\n+\n+\tlockdep_assert_held(\u0026zwplug-\u003elock);\n+\n+\tif (bio_list_empty(\u0026zwplug-\u003ebio_list))\n+\t\treturn;\n+\n+\tpr_warn_ratelimited(\"%s: zone %u: Aborting plugged BIOs\\n\",\n+\t\t\t zwplug-\u003edisk-\u003edisk_name, zwplug-\u003ezone_no);\n+\twhile ((bio = bio_list_pop(\u0026zwplug-\u003ebio_list)))\n+\t\tblk_zone_wplug_bio_io_error(zwplug, bio);\n+\n+\tzwplug-\u003eflags \u0026= ~BLK_ZONE_WPLUG_PLUGGED;\n+\n+\t/*\n+\t * If we are using the per disk zone write plugs worker thread, remove\n+\t * the zone write plug from the work list and drop the reference we\n+\t * took when the zone write plug was added to that list.\n+\t */\n+\tif (blk_queue_zoned_qd1_writes(disk-\u003equeue)) {\n+\t\tspin_lock(\u0026disk-\u003ezone_wplugs_list_lock);\n+\t\tif (!list_empty(\u0026zwplug-\u003eentry)) {\n+\t\t\tlist_del_init(\u0026zwplug-\u003eentry);\n+\t\t\tdisk_put_zone_wplug(zwplug);\n+\t\t}\n+\t\tspin_unlock(\u0026disk-\u003ezone_wplugs_list_lock);\n+\t}\n+}\n+\n /*\n * Flag the zone write plug as dead and drop the initial reference we got when\n * the zone write plug was added to the hash table. The zone write plug will be\n@@ -625,6 +817,12 @@ static void disk_mark_zone_wplug_dead(struct blk_zone_wplug *zwplug)\n \n static inline bool disk_check_zone_wplug_dead(struct blk_zone_wplug *zwplug)\n {\n+\tif (disk_zone_wplug_is_offline_or_readonly(zwplug)) {\n+\t\tdisk_zone_wplug_abort(zwplug);\n+\t\tdisk_mark_zone_wplug_dead(zwplug);\n+\t\treturn true;\n+\t}\n+\n \tif (!(zwplug-\u003eflags \u0026 BLK_ZONE_WPLUG_DEAD))\n \t\treturn false;\n \n@@ -708,53 +906,6 @@ static struct blk_zone_wplug *disk_get_or_alloc_zone_wplug(struct gendisk *disk,\n \treturn zwplug;\n }\n \n-static inline void blk_zone_wplug_bio_io_error(struct blk_zone_wplug *zwplug,\n-\t\t\t\t\t struct bio *bio)\n-{\n-\tstruct request_queue *q = zwplug-\u003edisk-\u003equeue;\n-\n-\tbio_clear_flag(bio, BIO_ZONE_WRITE_PLUGGING);\n-\tbio_io_error(bio);\n-\tdisk_put_zone_wplug(zwplug);\n-\t/* Drop the reference taken by disk_zone_wplug_add_bio(). */\n-\tblk_queue_exit(q);\n-}\n-\n-/*\n- * Abort (fail) all plugged BIOs of a zone write plug.\n- */\n-static void disk_zone_wplug_abort(struct blk_zone_wplug *zwplug)\n-{\n-\tstruct gendisk *disk = zwplug-\u003edisk;\n-\tstruct bio *bio;\n-\n-\tlockdep_assert_held(\u0026zwplug-\u003elock);\n-\n-\tif (bio_list_empty(\u0026zwplug-\u003ebio_list))\n-\t\treturn;\n-\n-\tpr_warn_ratelimited(\"%s: zone %u: Aborting plugged BIOs\\n\",\n-\t\t\t zwplug-\u003edisk-\u003edisk_name, zwplug-\u003ezone_no);\n-\twhile ((bio = bio_list_pop(\u0026zwplug-\u003ebio_list)))\n-\t\tblk_zone_wplug_bio_io_error(zwplug, bio);\n-\n-\tzwplug-\u003eflags \u0026= ~BLK_ZONE_WPLUG_PLUGGED;\n-\n-\t/*\n-\t * If we are using the per disk zone write plugs worker thread, remove\n-\t * the zone write plug from the work list and drop the reference we\n-\t * took when the zone write plug was added to that list.\n-\t */\n-\tif (blk_queue_zoned_qd1_writes(disk-\u003equeue)) {\n-\t\tspin_lock(\u0026disk-\u003ezone_wplugs_list_lock);\n-\t\tif (!list_empty(\u0026zwplug-\u003eentry)) {\n-\t\t\tlist_del_init(\u0026zwplug-\u003eentry);\n-\t\t\tdisk_put_zone_wplug(zwplug);\n-\t\t}\n-\t\tspin_unlock(\u0026disk-\u003ezone_wplugs_list_lock);\n-\t}\n-}\n-\n /*\n * Update a zone write plug condition based on the write pointer offset.\n */\n@@ -785,8 +936,10 @@ static void disk_zone_wplug_set_wp_offset(struct gendisk *disk,\n \n \t/* Update the zone write pointer and abort all plugged BIOs. */\n \tzwplug-\u003eflags \u0026= ~BLK_ZONE_WPLUG_NEED_WP_UPDATE;\n-\tzwplug-\u003ewp_offset = wp_offset;\n-\tdisk_zone_wplug_update_cond(disk, zwplug);\n+\tif (!disk_zone_wplug_is_offline_or_readonly(zwplug)) {\n+\t\tzwplug-\u003ewp_offset = wp_offset;\n+\t\tdisk_zone_wplug_update_cond(disk, zwplug);\n+\t}\n \n \tdisk_zone_wplug_abort(zwplug);\n \tif (!zwplug-\u003ewp_offset || disk_zone_wplug_is_full(disk, zwplug))\n@@ -816,8 +969,8 @@ static unsigned int blk_zone_wp_offset(struct blk_zone *zone)\n \t}\n }\n \n-static unsigned int disk_zone_wplug_sync_wp_offset(struct gendisk *disk,\n-\t\t\t\t\t\t struct blk_zone *zone)\n+static unsigned int disk_zone_wplug_sync_state(struct gendisk *disk,\n+\t\t\t\t\t struct blk_zone *zone)\n {\n \tstruct blk_zone_wplug *zwplug;\n \tunsigned int wp_offset = blk_zone_wp_offset(zone);\n@@ -829,6 +982,10 @@ static unsigned int disk_zone_wplug_sync_wp_offset(struct gendisk *disk,\n \t\tspin_lock_irqsave(\u0026zwplug-\u003elock, flags);\n \t\tif (zwplug-\u003eflags \u0026 BLK_ZONE_WPLUG_NEED_WP_UPDATE)\n \t\t\tdisk_zone_wplug_set_wp_offset(disk, zwplug, wp_offset);\n+\t\tif (disk_zone_cond_is_offline_or_readonly(zone-\u003econd)) {\n+\t\t\tzwplug-\u003econd = zone-\u003econd;\n+\t\t\tdisk_mark_zone_wplug_dead(zwplug);\n+\t\t}\n \t\tspin_unlock_irqrestore(\u0026zwplug-\u003elock, flags);\n \t\tdisk_put_zone_wplug(zwplug);\n \t}\n@@ -871,7 +1028,7 @@ int disk_report_zone(struct gendisk *disk, struct blk_zone *zone,\n \t}\n \n \tif (disk-\u003ezone_wplugs_hash)\n-\t\tdisk_zone_wplug_sync_wp_offset(disk, zone);\n+\t\tdisk_zone_wplug_sync_state(disk, zone);\n \n \tif (args \u0026\u0026 args-\u003ecb)\n \t\treturn args-\u003ecb(zone, idx, args-\u003edata);\n@@ -940,7 +1097,7 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,\n \tsector_t zone_sectors = bdev_zone_sectors(bdev);\n \tstruct blk_zone_wplug *zwplug;\n \tunsigned long flags;\n-\tu8 *zones_cond;\n+\tu8 *zones_state, zs;\n \n \tif (!bdev_is_zoned(bdev))\n \t\treturn -EOPNOTSUPP;\n@@ -955,12 +1112,18 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,\n \t\treturn blkdev_report_zone_fallback(bdev, sector, zone);\n \n \trcu_read_lock();\n-\tzones_cond = rcu_dereference(disk-\u003ezones_cond);\n-\tif (!disk-\u003ezone_wplugs_hash || !zones_cond) {\n+\tzones_state = rcu_dereference(disk-\u003ezones_state);\n+\tif (!disk-\u003ezone_wplugs_hash || !zones_state) {\n \t\trcu_read_unlock();\n \t\treturn blkdev_report_zone_fallback(bdev, sector, zone);\n \t}\n-\tzone-\u003econd = zones_cond[disk_zone_no(disk, sector)];\n+\n+\tzs = zones_state[disk_zone_no(disk, sector)];\n+\tzone-\u003econd = blk_zstate_to_zone_cond(zs);\n+\tif (blk_zstate_is_conv(zs))\n+\t\tzone-\u003etype = BLK_ZONE_TYPE_CONVENTIONAL;\n+\telse\n+\t\tzone-\u003etype = BLK_ZONE_TYPE_SEQWRITE_REQ;\n \trcu_read_unlock();\n \n \tzone-\u003estart = sector;\n@@ -970,8 +1133,7 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,\n \t * If this is a conventional zone, we do not have a zone write plug and\n \t * can report the zone immediately.\n \t */\n-\tif (zone-\u003econd == BLK_ZONE_COND_NOT_WP) {\n-\t\tzone-\u003etype = BLK_ZONE_TYPE_CONVENTIONAL;\n+\tif (zone-\u003etype == BLK_ZONE_TYPE_CONVENTIONAL) {\n \t\tzone-\u003ecapacity = zone_sectors;\n \t\tzone-\u003ewp = ULLONG_MAX;\n \t\treturn 0;\n@@ -982,14 +1144,12 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,\n \t * offline, only set the zone write pointer to an invalid value and\n \t * report the zone.\n \t */\n-\tzone-\u003etype = BLK_ZONE_TYPE_SEQWRITE_REQ;\n \tif (disk_zone_is_last(disk, zone))\n \t\tzone-\u003ecapacity = disk-\u003elast_zone_capacity;\n \telse\n \t\tzone-\u003ecapacity = disk-\u003ezone_capacity;\n \n-\tif (zone-\u003econd == BLK_ZONE_COND_READONLY ||\n-\t zone-\u003econd == BLK_ZONE_COND_OFFLINE) {\n+\tif (disk_zone_cond_is_offline_or_readonly(zone-\u003econd)) {\n \t\tzone-\u003ewp = ULLONG_MAX;\n \t\treturn 0;\n \t}\n@@ -1108,34 +1268,32 @@ static void blk_zone_reset_bio_endio(struct bio *bio)\n \t}\n }\n \n+static void disk_zone_wplug_reset_wp(struct blk_zone_wplug *zwplug, void *data)\n+{\n+\tunsigned long flags;\n+\n+\tspin_lock_irqsave(\u0026zwplug-\u003elock, flags);\n+\tdisk_zone_wplug_set_wp_offset(zwplug-\u003edisk, zwplug, 0);\n+\tspin_unlock_irqrestore(\u0026zwplug-\u003elock, flags);\n+}\n+\n static void blk_zone_reset_all_bio_endio(struct bio *bio)\n {\n \tstruct gendisk *disk = bio-\u003ebi_bdev-\u003ebd_disk;\n-\tsector_t capacity = get_capacity(disk);\n-\tstruct blk_zone_wplug *zwplug;\n-\tunsigned long flags;\n \tsector_t sector;\n-\tunsigned int i;\n \n-\tif (atomic_read(\u0026disk-\u003enr_zone_wplugs)) {\n-\t\t/* Update the condition of all zone write plugs. */\n-\t\trcu_read_lock();\n-\t\tfor (i = 0; i \u003c disk_zone_wplugs_hash_size(disk); i++) {\n-\t\t\thlist_for_each_entry_rcu(zwplug,\n-\t\t\t\t\t\t \u0026disk-\u003ezone_wplugs_hash[i],\n-\t\t\t\t\t\t node) {\n-\t\t\t\tspin_lock_irqsave(\u0026zwplug-\u003elock, flags);\n-\t\t\t\tdisk_zone_wplug_set_wp_offset(disk, zwplug, 0);\n-\t\t\t\tspin_unlock_irqrestore(\u0026zwplug-\u003elock, flags);\n-\t\t\t}\n-\t\t}\n-\t\trcu_read_unlock();\n-\t}\n+\t/* Update the condition of all zone write plugs. */\n+\tif (atomic_read(\u0026disk-\u003enr_zone_wplugs))\n+\t\tdisk_for_all_zone_wplugs(disk, disk_zone_wplug_reset_wp, NULL);\n \n \t/* Update the cached zone conditions. */\n-\tfor (sector = 0; sector \u003c capacity;\n-\t sector += bdev_zone_sectors(bio-\u003ebi_bdev))\n+\tfor (sector = 0; sector \u003c get_capacity(disk);\n+\t sector += bdev_zone_sectors(bio-\u003ebi_bdev)) {\n+\t\tif (!disk_zone_is_seq(disk, sector) ||\n+\t\t disk_zone_is_offline_or_readonly(disk, sector))\n+\t\t\tcontinue;\n \t\tdisk_zone_set_cond(disk, sector, BLK_ZONE_COND_EMPTY);\n+\t}\n \tclear_bit(GD_ZONE_APPEND_USED, \u0026disk-\u003estate);\n }\n \n@@ -1381,11 +1539,12 @@ static bool blk_zone_wplug_prepare_bio(struct blk_zone_wplug *zwplug,\n \t\treturn false;\n \n \t/*\n-\t * Check that the user is not attempting to write to a full zone.\n-\t * We know such BIO will fail, and that would potentially overflow our\n-\t * write pointer offset beyond the end of the zone.\n+\t * Check that the user is not attempting to write to a full, read-only\n+\t * or offline zone. We know such BIOs will fail, so there is no point\n+\t * in issuing them.\n \t */\n-\tif (disk_zone_wplug_is_full(disk, zwplug))\n+\tif (disk_zone_wplug_is_full(disk, zwplug) ||\n+\t disk_zone_wplug_is_offline_or_readonly(zwplug))\n \t\treturn false;\n \n \tif (bio_op(bio) == REQ_OP_ZONE_APPEND) {\n@@ -1442,7 +1601,7 @@ static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs)\n \t}\n \n \t/* Conventional zones do not need write plugging. */\n-\tif (!bdev_zone_is_seq(bio-\u003ebi_bdev, sector)) {\n+\tif (!disk_zone_is_seq(disk, sector)) {\n \t\t/* Zone append to conventional zones is not allowed. */\n \t\tif (bio_op(bio) == REQ_OP_ZONE_APPEND) {\n \t\t\tbio_io_error(bio);\n@@ -1854,12 +2013,20 @@ static int disk_zone_wplugs_worker(void *data)\n \n void disk_init_zone_resources(struct gendisk *disk)\n {\n+\tatomic_set(\u0026disk-\u003enr_zone_wplugs, 0);\n \tspin_lock_init(\u0026disk-\u003ezone_wplugs_hash_lock);\n \tspin_lock_init(\u0026disk-\u003ezone_wplugs_list_lock);\n \tINIT_LIST_HEAD(\u0026disk-\u003ezone_wplugs_list);\n \tinit_completion(\u0026disk-\u003ezone_wplugs_worker_bio_done);\n }\n \n+static unsigned int disk_get_nr_zones(struct gendisk *disk)\n+{\n+\tstruct queue_limits *lim = \u0026disk-\u003equeue-\u003elimits;\n+\n+\treturn DIV_ROUND_UP_ULL(get_capacity(disk), lim-\u003echunk_sectors);\n+}\n+\n /*\n * For the size of a disk zone write plug hash table, use the size of the\n * zone write plug mempool, which is the maximum of the disk open zones and\n@@ -1869,13 +2036,21 @@ void disk_init_zone_resources(struct gendisk *disk)\n #define BLK_ZONE_WPLUG_MAX_HASH_BITS\t\t9\n #define BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE\t128\n \n-static int disk_alloc_zone_resources(struct gendisk *disk,\n-\t\t\t\t unsigned int pool_size)\n+static int disk_alloc_zone_resources(struct gendisk *disk)\n {\n-\tunsigned int i;\n+\tstruct queue_limits *lim = \u0026disk-\u003equeue-\u003elimits;\n+\tunsigned int nr_zones = disk_get_nr_zones(disk);\n+\tunsigned int pool_size, i;\n \tint ret = -ENOMEM;\n \n-\tatomic_set(\u0026disk-\u003enr_zone_wplugs, 0);\n+\t/*\n+\t * If the device has no limit on the maximum number of open and active\n+\t * zones, use BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE.\n+\t */\n+\tpool_size = max(lim-\u003emax_open_zones, lim-\u003emax_active_zones);\n+\tif (!pool_size)\n+\t\tpool_size = min(BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE, nr_zones);\n+\n \tdisk-\u003ezone_wplugs_hash_bits =\n \t\tmin(ilog2(pool_size) + 1, BLK_ZONE_WPLUG_MAX_HASH_BITS);\n \n@@ -1893,21 +2068,6 @@ static int disk_alloc_zone_resources(struct gendisk *disk,\n \tif (!disk-\u003ezone_wplugs_pool)\n \t\tgoto free_hash;\n \n-\t/*\n-\t * We may already have a zone write plug workqueue as this function may\n-\t * be called after disk_free_zone_resources(), which does not destroy\n-\t * the workqueue (the zone write plugs workqueue is destroyed at\n-\t * disk_release() time).\n-\t */\n-\tif (!disk-\u003ezone_wplugs_wq) {\n-\t\tdisk-\u003ezone_wplugs_wq =\n-\t\t\talloc_workqueue(\"%s_zwplugs\",\n-\t\t\t\t\tWQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,\n-\t\t\t\t\tpool_size, disk-\u003edisk_name);\n-\t\tif (!disk-\u003ezone_wplugs_wq)\n-\t\t\tgoto destroy_pool;\n-\t}\n-\n \tdisk-\u003ezone_wplugs_worker =\n \t\tkthread_create(disk_zone_wplugs_worker, disk,\n \t\t\t \"%s_zwplugs_worker\", disk-\u003edisk_name);\n@@ -1918,8 +2078,18 @@ static int disk_alloc_zone_resources(struct gendisk *disk,\n \t}\n \twake_up_process(disk-\u003ezone_wplugs_worker);\n \n+\tdisk-\u003ezone_wplugs_wq =\n+\t\talloc_workqueue(\"%s_zwplugs\",\n+\t\t\t\tWQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,\n+\t\t\t\tpool_size, disk-\u003edisk_name);\n+\tif (!disk-\u003ezone_wplugs_wq)\n+\t\tgoto stop_worker;\n+\n \treturn 0;\n \n+stop_worker:\n+\tkthread_stop(disk-\u003ezone_wplugs_worker);\n+\tdisk-\u003ezone_wplugs_worker = NULL;\n destroy_pool:\n \tmempool_destroy(disk-\u003ezone_wplugs_pool);\n \tdisk-\u003ezone_wplugs_pool = NULL;\n@@ -1963,19 +2133,19 @@ static void disk_destroy_zone_wplugs_hash_table(struct gendisk *disk)\n \tdisk-\u003ezone_wplugs_pool = NULL;\n }\n \n-static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond)\n+static void disk_set_zones_state_array(struct gendisk *disk, u8 *zones_state)\n {\n \tunsigned long flags;\n \n \tspin_lock_irqsave(\u0026disk-\u003ezone_wplugs_hash_lock, flags);\n-\tzones_cond = rcu_replace_pointer(disk-\u003ezones_cond, zones_cond,\n+\tzones_state = rcu_replace_pointer(disk-\u003ezones_state, zones_state,\n \t\t\t\tlockdep_is_held(\u0026disk-\u003ezone_wplugs_hash_lock));\n \tspin_unlock_irqrestore(\u0026disk-\u003ezone_wplugs_hash_lock, flags);\n \n-\tkfree_rcu_mightsleep(zones_cond);\n+\tkfree_rcu_mightsleep(zones_state);\n }\n \n-static void disk_free_zone_resources(struct gendisk *disk)\n+void disk_release_zone_resources(struct gendisk *disk)\n {\n \tif (disk-\u003ezone_wplugs_worker) {\n \t\tkthread_stop(disk-\u003ezone_wplugs_worker);\n@@ -1983,30 +2153,22 @@ static void disk_free_zone_resources(struct gendisk *disk)\n \t}\n \tWARN_ON_ONCE(!list_empty(\u0026disk-\u003ezone_wplugs_list));\n \n-\tif (disk-\u003ezone_wplugs_wq)\n-\t\tdrain_workqueue(disk-\u003ezone_wplugs_wq);\n+\tif (disk-\u003ezone_wplugs_wq) {\n+\t\tdestroy_workqueue(disk-\u003ezone_wplugs_wq);\n+\t\tdisk-\u003ezone_wplugs_wq = NULL;\n+\t}\n \n \tdisk_destroy_zone_wplugs_hash_table(disk);\n \n-\tdisk_set_zones_cond_array(disk, NULL);\n+\tdisk_set_zones_state_array(disk, NULL);\n \tdisk-\u003ezone_capacity = 0;\n \tdisk-\u003elast_zone_capacity = 0;\n \tdisk-\u003enr_zones = 0;\n }\n \n-void disk_release_zone_resources(struct gendisk *disk)\n-{\n-\tif (disk-\u003ezone_wplugs_wq) {\n-\t\tdestroy_workqueue(disk-\u003ezone_wplugs_wq);\n-\t\tdisk-\u003ezone_wplugs_wq = NULL;\n-\t}\n-\n-\tdisk_free_zone_resources(disk);\n-}\n-\n struct blk_revalidate_zone_args {\n \tstruct gendisk\t*disk;\n-\tu8\t\t*zones_cond;\n+\tu8\t\t*zones_state;\n \tunsigned int\tnr_zones;\n \tunsigned int\tnr_conv_zones;\n \tunsigned int\tzone_capacity;\n@@ -2014,49 +2176,26 @@ struct blk_revalidate_zone_args {\n \tsector_t\tsector;\n };\n \n-static int disk_revalidate_zone_resources(struct gendisk *disk,\n+static int disk_init_revalidate_args(struct gendisk *disk,\n \t\t\t\tstruct blk_revalidate_zone_args *args)\n {\n-\tstruct queue_limits *lim = \u0026disk-\u003equeue-\u003elimits;\n-\tunsigned int pool_size;\n-\tint ret = 0;\n-\n \targs-\u003edisk = disk;\n-\targs-\u003enr_zones =\n-\t\tDIV_ROUND_UP_ULL(get_capacity(disk), lim-\u003echunk_sectors);\n+\targs-\u003enr_zones = disk_get_nr_zones(disk);\n \n \t/* Cached zone conditions: 1 byte per zone */\n-\targs-\u003ezones_cond = kzalloc(args-\u003enr_zones, GFP_NOIO);\n-\tif (!args-\u003ezones_cond)\n+\targs-\u003ezones_state = kzalloc(args-\u003enr_zones, GFP_NOIO);\n+\tif (!args-\u003ezones_state)\n \t\treturn -ENOMEM;\n \n-\tif (!disk_need_zone_resources(disk))\n-\t\treturn 0;\n-\n-\t/*\n-\t * If the device has no limit on the maximum number of open and active\n-\t * zones, use BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE.\n-\t */\n-\tpool_size = max(lim-\u003emax_open_zones, lim-\u003emax_active_zones);\n-\tif (!pool_size)\n-\t\tpool_size =\n-\t\t\tmin(BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE, args-\u003enr_zones);\n-\n-\tif (!disk-\u003ezone_wplugs_hash) {\n-\t\tret = disk_alloc_zone_resources(disk, pool_size);\n-\t\tif (ret)\n-\t\t\tkfree(args-\u003ezones_cond);\n-\t}\n-\n-\treturn ret;\n+\treturn 0;\n }\n \n /*\n- * Update the disk zone resources information and device queue limits.\n- * The disk queue is frozen when this is executed.\n+ * Revalidate and update the disk zone resources information and device queue\n+ * limits.\n */\n-static int disk_update_zone_resources(struct gendisk *disk,\n-\t\t\t\t struct blk_revalidate_zone_args *args)\n+static int disk_revalidate_zone_resources(struct gendisk *disk,\n+\t\t\t\t\t struct blk_revalidate_zone_args *args)\n {\n \tstruct request_queue *q = disk-\u003equeue;\n \tunsigned int nr_seq_zones;\n@@ -2064,23 +2203,28 @@ static int disk_update_zone_resources(struct gendisk *disk,\n \tstruct queue_limits lim;\n \tint ret = 0;\n \n+\t/* Make sure that the entire disk capacity has been checked. */\n+\tif (args-\u003esector != get_capacity(disk)) {\n+\t\tpr_warn(\"%s: Missing zones from sector %llu\\n\",\n+\t\t\tdisk-\u003edisk_name, args-\u003esector);\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tif (args-\u003enr_conv_zones \u003e= args-\u003enr_zones) {\n+\t\tpr_warn(\"%s: Invalid number of conventional zones %u / %u\\n\",\n+\t\t\tdisk-\u003edisk_name, args-\u003enr_conv_zones, args-\u003enr_zones);\n+\t\treturn -ENODEV;\n+\t}\n+\n \tlim = queue_limits_start_update(q);\n \n \tmemflags = blk_mq_freeze_queue(q);\n \n \tdisk-\u003enr_zones = args-\u003enr_zones;\n-\tif (args-\u003enr_conv_zones \u003e= disk-\u003enr_zones) {\n-\t\tqueue_limits_cancel_update(q);\n-\t\tpr_warn(\"%s: Invalid number of conventional zones %u / %u\\n\",\n-\t\t\tdisk-\u003edisk_name, args-\u003enr_conv_zones, disk-\u003enr_zones);\n-\t\tret = -ENODEV;\n-\t\tgoto unfreeze;\n-\t}\n-\n \tdisk-\u003ezone_capacity = args-\u003ezone_capacity;\n \tdisk-\u003elast_zone_capacity = args-\u003elast_zone_capacity;\n-\tdisk_set_zones_cond_array(disk, args-\u003ezones_cond);\n-\targs-\u003ezones_cond = NULL;\n+\tdisk_set_zones_state_array(disk, args-\u003ezones_state);\n+\targs-\u003ezones_state = NULL;\n \n \t/*\n \t * Some devices can advertise zone resource limits that are larger than\n@@ -2120,48 +2264,59 @@ static int disk_update_zone_resources(struct gendisk *disk,\n commit:\n \tret = queue_limits_commit_update(q, \u0026lim);\n \n-unfreeze:\n \tblk_mq_unfreeze_queue(q, memflags);\n \n \treturn ret;\n }\n \n-static int blk_revalidate_zone_cond(struct blk_zone *zone, unsigned int idx,\n-\t\t\t\t struct blk_revalidate_zone_args *args)\n+static void disk_drop_zone_wplug(struct blk_zone_wplug *zwplug, void *data)\n {\n-\tenum blk_zone_cond cond = zone-\u003econd;\n+\tunsigned long flags;\n \n-\t/* Check that the zone condition is consistent with the zone type. */\n-\tswitch (cond) {\n-\tcase BLK_ZONE_COND_NOT_WP:\n-\t\tif (zone-\u003etype != BLK_ZONE_TYPE_CONVENTIONAL)\n-\t\t\tgoto invalid_condition;\n-\t\tbreak;\n-\tcase BLK_ZONE_COND_IMP_OPEN:\n-\tcase BLK_ZONE_COND_EXP_OPEN:\n-\tcase BLK_ZONE_COND_CLOSED:\n-\tcase BLK_ZONE_COND_EMPTY:\n-\tcase BLK_ZONE_COND_FULL:\n-\tcase BLK_ZONE_COND_OFFLINE:\n-\tcase BLK_ZONE_COND_READONLY:\n-\t\tif (zone-\u003etype != BLK_ZONE_TYPE_SEQWRITE_REQ)\n-\t\t\tgoto invalid_condition;\n-\t\tbreak;\n-\tdefault:\n-\t\tpr_warn(\"%s: Invalid zone condition 0x%X\\n\",\n-\t\t\targs-\u003edisk-\u003edisk_name, cond);\n-\t\treturn -ENODEV;\n+\tspin_lock_irqsave(\u0026zwplug-\u003elock, flags);\n+\tdisk_zone_wplug_abort(zwplug);\n+\tdisk_mark_zone_wplug_dead(zwplug);\n+\tspin_unlock_irqrestore(\u0026zwplug-\u003elock, flags);\n+}\n+\n+static int disk_revalidate_capacity(struct gendisk *disk)\n+{\n+\tstruct queue_limits *lim = \u0026disk-\u003equeue-\u003elimits;\n+\tsector_t zone_sectors = lim-\u003echunk_sectors;\n+\tunsigned int nr_zones = disk_get_nr_zones(disk);\n+\tint ret = -ENODEV;\n+\n+\tif (!get_capacity(disk))\n+\t\tgoto drop_all_zwplugs;\n+\n+\t/*\n+\t * Checks that the device driver indicated a valid zone size and that\n+\t * the max zone append limit is set.\n+\t */\n+\tif (!zone_sectors || !is_power_of_2(zone_sectors)) {\n+\t\tpr_warn(\"%s: Invalid non power of two zone size (%llu)\\n\",\n+\t\t\tdisk-\u003edisk_name, zone_sectors);\n+\t\tgoto drop_all_zwplugs;\n \t}\n \n-\tblk_zone_set_cond(args-\u003ezones_cond, idx, cond);\n+\t/*\n+\t * Check if the capacity has changed. If it did, assume that the device\n+\t * was reformatted and that all sequential zones are now empty. So drop\n+\t * all zone write plug.\n+\t */\n+\tif (disk-\u003enr_zones \u0026\u0026 disk-\u003enr_zones != nr_zones) {\n+\t\tpr_warn(\"%s: Number of zones changed (%u -\u003e %u)\\n\",\n+\t\t\tdisk-\u003edisk_name, disk-\u003enr_zones, nr_zones);\n+\t\tret = 0;\n+\t\tgoto drop_all_zwplugs;\n+\t}\n \n \treturn 0;\n \n-invalid_condition:\n-\tpr_warn(\"%s: Invalid zone condition 0x%x for type 0x%x\\n\",\n-\t\targs-\u003edisk-\u003edisk_name, cond, zone-\u003etype);\n+drop_all_zwplugs:\n+\tdisk_for_all_zone_wplugs(disk, disk_drop_zone_wplug, NULL);\n \n-\treturn -ENODEV;\n+\treturn ret;\n }\n \n static int blk_revalidate_conv_zone(struct blk_zone *zone, unsigned int idx,\n@@ -2169,12 +2324,26 @@ static int blk_revalidate_conv_zone(struct blk_zone *zone, unsigned int idx,\n {\n \tstruct gendisk *disk = args-\u003edisk;\n \n+\t/* Check the zone condition. */\n+\tswitch (zone-\u003econd) {\n+\tcase BLK_ZONE_COND_NOT_WP:\n+\tcase BLK_ZONE_COND_OFFLINE:\n+\tcase BLK_ZONE_COND_READONLY:\n+\t\tbreak;\n+\tdefault:\n+\t\tpr_warn(\"%s: Invalid conv. zone condition 0x%X at sector %llu\\n\",\n+\t\t\tdisk-\u003edisk_name, zone-\u003econd, zone-\u003estart);\n+\t\treturn -ENODEV;\n+\t}\n+\n \tif (zone-\u003ecapacity != zone-\u003elen) {\n \t\tpr_warn(\"%s: Invalid conventional zone capacity\\n\",\n \t\t\tdisk-\u003edisk_name);\n \t\treturn -ENODEV;\n \t}\n \n+\tblk_zstate_set(args-\u003ezones_state, idx, zone-\u003econd, BLK_ZFLAG_CONV);\n+\n \tif (disk_zone_is_last(disk, zone))\n \t\targs-\u003elast_zone_capacity = zone-\u003ecapacity;\n \n@@ -2190,6 +2359,24 @@ static int blk_revalidate_seq_zone(struct blk_zone *zone, unsigned int idx,\n \tstruct blk_zone_wplug *zwplug;\n \tunsigned int wp_offset;\n \n+\t/* Check the zone condition. */\n+\tswitch (zone-\u003econd) {\n+\tcase BLK_ZONE_COND_IMP_OPEN:\n+\tcase BLK_ZONE_COND_EXP_OPEN:\n+\tcase BLK_ZONE_COND_CLOSED:\n+\tcase BLK_ZONE_COND_EMPTY:\n+\tcase BLK_ZONE_COND_FULL:\n+\tcase BLK_ZONE_COND_OFFLINE:\n+\tcase BLK_ZONE_COND_READONLY:\n+\t\tbreak;\n+\tdefault:\n+\t\tpr_warn(\"%s: Invalid seq. zone condition 0x%X at sector %llu\\n\",\n+\t\t\tdisk-\u003edisk_name, zone-\u003econd, zone-\u003estart);\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tblk_zstate_set(args-\u003ezones_state, idx, zone-\u003econd, 0);\n+\n \t/*\n \t * Remember the capacity of the first sequential zone and check\n \t * if it is constant for all zones, ignoring the last zone as it can be\n@@ -2214,7 +2401,7 @@ static int blk_revalidate_seq_zone(struct blk_zone *zone, unsigned int idx,\n \tif (!disk-\u003ezone_wplugs_hash)\n \t\treturn 0;\n \n-\twp_offset = disk_zone_wplug_sync_wp_offset(disk, zone);\n+\twp_offset = disk_zone_wplug_sync_state(disk, zone);\n \tif (!wp_offset || wp_offset \u003e= zone-\u003ecapacity)\n \t\treturn 0;\n \n@@ -2272,11 +2459,6 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,\n \t\treturn -ENODEV;\n \t}\n \n-\t/* Check zone condition */\n-\tret = blk_revalidate_zone_cond(zone, idx, args);\n-\tif (ret)\n-\t\treturn ret;\n-\n \t/* Check zone type */\n \tswitch (zone-\u003etype) {\n \tcase BLK_ZONE_TYPE_CONVENTIONAL:\n@@ -2313,39 +2495,37 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,\n */\n int blk_revalidate_disk_zones(struct gendisk *disk)\n {\n-\tstruct request_queue *q = disk-\u003equeue;\n-\tsector_t zone_sectors = q-\u003elimits.chunk_sectors;\n-\tsector_t capacity = get_capacity(disk);\n \tstruct blk_revalidate_zone_args args = { };\n-\tunsigned int memflags, noio_flag;\n \tstruct blk_report_zones_args rep_args = {\n \t\t.cb = blk_revalidate_zone_cb,\n \t\t.data = \u0026args,\n \t};\n-\tint ret = -ENOMEM;\n+\tunsigned int noio_flag;\n+\tint ret;\n \n-\tif (WARN_ON_ONCE(!blk_queue_is_zoned(q)))\n+\tif (WARN_ON_ONCE(!blk_queue_is_zoned(disk-\u003equeue)))\n \t\treturn -EIO;\n \n-\tif (!capacity)\n-\t\treturn -ENODEV;\n+\tret = disk_revalidate_capacity(disk);\n+\tif (ret)\n+\t\treturn ret;\n \n \t/*\n-\t * Checks that the device driver indicated a valid zone size and that\n-\t * the max zone append limit is set.\n+\t * Allocate zone resources if they are needed and we have not done\n+\t * so yet, and initialize the revalidation arguments passed to report\n+\t * zones. Ensure that all memory allocations in this context are done as\n+\t * if GFP_NOIO was specified.\n \t */\n-\tif (!zone_sectors || !is_power_of_2(zone_sectors)) {\n-\t\tpr_warn(\"%s: Invalid non power of two zone size (%llu)\\n\",\n-\t\t\tdisk-\u003edisk_name, zone_sectors);\n-\t\treturn -ENODEV;\n+\tnoio_flag = memalloc_noio_save();\n+\tif (disk_need_zone_resources(disk) \u0026\u0026 !disk-\u003ezone_wplugs_hash) {\n+\t\tret = disk_alloc_zone_resources(disk);\n+\t\tif (ret) {\n+\t\t\tmemalloc_noio_restore(noio_flag);\n+\t\t\treturn ret;\n+\t\t}\n \t}\n \n-\t/*\n-\t * Ensure that all memory allocations in this context are done as if\n-\t * GFP_NOIO was specified.\n-\t */\n-\tnoio_flag = memalloc_noio_save();\n-\tret = disk_revalidate_zone_resources(disk, \u0026args);\n+\tret = disk_init_revalidate_args(disk, \u0026args);\n \tif (ret) {\n \t\tmemalloc_noio_restore(noio_flag);\n \t\treturn ret;\n@@ -2359,32 +2539,18 @@ int blk_revalidate_disk_zones(struct gendisk *disk)\n \tmemalloc_noio_restore(noio_flag);\n \n \tif (ret \u003c= 0)\n-\t\tgoto free_resources;\n+\t\tgoto free_args;\n \n-\t/*\n-\t * If zones where reported, make sure that the entire disk capacity\n-\t * has been checked.\n-\t */\n-\tif (args.sector != capacity) {\n-\t\tpr_warn(\"%s: Missing zones from sector %llu\\n\",\n-\t\t\tdisk-\u003edisk_name, args.sector);\n-\t\tret = -ENODEV;\n-\t\tgoto free_resources;\n-\t}\n-\n-\tret = disk_update_zone_resources(disk, \u0026args);\n+\tret = disk_revalidate_zone_resources(disk, \u0026args);\n \tif (ret)\n-\t\tgoto free_resources;\n+\t\tgoto free_args;\n \n \treturn 0;\n \n-free_resources:\n+free_args:\n \tpr_warn(\"%s: failed to revalidate zones\\n\", disk-\u003edisk_name);\n \n-\tkfree(args.zones_cond);\n-\tmemflags = blk_mq_freeze_queue(q);\n-\tdisk_free_zone_resources(disk);\n-\tblk_mq_unfreeze_queue(q, memflags);\n+\tkfree(args.zones_state);\n \n \treturn ret;\n }\n@@ -2435,8 +2601,9 @@ EXPORT_SYMBOL_GPL(blk_zone_issue_zeroout);\n \n #ifdef CONFIG_BLK_DEBUG_FS\n static void queue_zone_wplug_show(struct blk_zone_wplug *zwplug,\n-\t\t\t\t struct seq_file *m)\n+\t\t\t\t void *data)\n {\n+\tstruct seq_file *m = data;\n \tunsigned int zwp_wp_offset, zwp_flags;\n \tunsigned int zwp_zone_no, zwp_ref;\n \tunsigned int zwp_bio_list_size;\n@@ -2461,19 +2628,8 @@ static void queue_zone_wplug_show(struct blk_zone_wplug *zwplug,\n int queue_zone_wplugs_show(void *data, struct seq_file *m)\n {\n \tstruct request_queue *q = data;\n-\tstruct gendisk *disk = q-\u003edisk;\n-\tstruct blk_zone_wplug *zwplug;\n-\tunsigned int i;\n-\n-\tif (!disk-\u003ezone_wplugs_hash)\n-\t\treturn 0;\n \n-\trcu_read_lock();\n-\tfor (i = 0; i \u003c disk_zone_wplugs_hash_size(disk); i++)\n-\t\thlist_for_each_entry_rcu(zwplug, \u0026disk-\u003ezone_wplugs_hash[i],\n-\t\t\t\t\t node)\n-\t\t\tqueue_zone_wplug_show(zwplug, m);\n-\trcu_read_unlock();\n+\tdisk_for_all_zone_wplugs(q-\u003edisk, queue_zone_wplug_show, m);\n \n \treturn 0;\n }\ndiff --git a/block/blk.h b/block/blk.h\nindex 50abfd9328861..cfe8d4185e25a 100644\n--- a/block/blk.h\n+++ b/block/blk.h\n@@ -577,6 +577,7 @@ int blkdev_report_zones_ioctl(struct block_device *bdev, unsigned int cmd,\n \t\tunsigned long arg);\n int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,\n \t\tunsigned int cmd, unsigned long arg);\n+bool bdev_check_zone_mgmt(struct block_device *bdev, sector_t sector);\n #else /* CONFIG_BLK_DEV_ZONED */\n static inline void disk_init_zone_resources(struct gendisk *disk)\n {\n@@ -619,6 +620,11 @@ static inline int blkdev_zone_mgmt_ioctl(struct block_device *bdev,\n {\n \treturn -ENOTTY;\n }\n+static inline bool bdev_check_zone_mgmt(struct block_device *bdev,\n+\t\t\t\t\tsector_t sector)\n+{\n+\treturn false;\n+}\n #endif /* CONFIG_BLK_DEV_ZONED */\n \n struct block_device *bdev_alloc(struct gendisk *disk, u8 partno);\ndiff --git a/block/genhd.c b/block/genhd.c\nindex e8ce0cabf392c..34a64077ba02c 100644\n--- a/block/genhd.c\n+++ b/block/genhd.c\n@@ -447,6 +447,13 @@ static int __add_disk(struct device *parent, struct gendisk *disk,\n \t\tbdev_set_flag(disk-\u003epart0, BD_HAS_SUBMIT_BIO);\n \t}\n \n+\t/*\n+\t * We do not support partitions with zoned block devices, so do not try\n+\t * to scan the partitions table.\n+\t */\n+\tif (blk_queue_is_zoned(disk-\u003equeue))\n+\t\tdisk-\u003eflags |= GENHD_FL_NO_PART;\n+\n \t/*\n \t * If the driver provides an explicit major number it also must provide\n \t * the number of minors numbers supported, and those will be used to\ndiff --git a/include/linux/blkdev.h b/include/linux/blkdev.h\nindex 20cb8ed7d987a..4fa00757527d7 100644\n--- a/include/linux/blkdev.h\n+++ b/include/linux/blkdev.h\n@@ -200,7 +200,7 @@ struct gendisk {\n \tunsigned int\t\tnr_zones;\n \tunsigned int\t\tzone_capacity;\n \tunsigned int\t\tlast_zone_capacity;\n-\tu8 __rcu\t\t*zones_cond;\n+\tu8 __rcu\t\t*zones_state;\n \tunsigned int\t\tzone_wplugs_hash_bits;\n \tatomic_t\t\tnr_zone_wplugs;\n \tspinlock_t\t\tzone_wplugs_hash_lock;\n"
} |
| 2/1 | 2026/08/06 19:46 | agent | patch-evaluator |
1mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"BLK_DEV_ZONED",
"BLK_DEV_NULL_BLK",
"BLK_DEV_ZONED_LOOP"
],
"FocusSymbols": [
"bdev_check_zone_mgmt",
"blkdev_get_zone_info",
"blk_revalidate_disk_zones",
"disk_release_zone_resources",
"disk_init_zone_resources"
],
"Reasoning": "The patch refactors how zone conditions are tracked in the block layer, introducing `enum blk_zstate` and a new `zones_state` array to replace `zones_cond`. It updates zone management checks (`bdev_check_zone_mgmt`), zone revalidation (`blk_revalidate_disk_zones`), and zone information retrieval (`blkdev_get_zone_info`). It also modifies `__add_disk` to disable partitions on zoned block devices. These are functional changes to the core block layer's zoned storage support and should be fuzzed to ensure no regressions in zone state tracking or bio submission.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit c0528e68e9702e1ffdef1d23f21f27247d7e674a
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Aug 6 19:46:22 2026 +0000
syz-cluster: applied patch under review
diff --git a/block/blk-core.c b/block/blk-core.c
index 196bccf27f58d..80a4e2bcc3f34 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -901,8 +901,11 @@ void submit_bio_noacct(struct bio *bio)
case REQ_OP_ZONE_CLOSE:
case REQ_OP_ZONE_RESET:
case REQ_OP_ZONE_FINISH:
- /* Zone management operations require sequential zones. */
- if (!bdev_zone_is_seq(bio->bi_bdev, bio->bi_iter.bi_sector))
+ /*
+ * Zone management operations require sequential zones that are
+ * not offline nor read-only.
+ */
+ if (!bdev_check_zone_mgmt(bdev, bio->bi_iter.bi_sector))
goto end_io;
break;
case REQ_OP_ZONE_RESET_ALL:
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index a5afb842bf35e..f17069309ac9d 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -39,6 +39,103 @@ static const char *const zone_cond_name[] = {
};
#undef ZONE_COND_NAME
+/*
+ * Internal and compact representation of enum blk_zone_cond values for zone
+ * conditions. All these values fit into 4-bits, allowing using the high order
+ * bits as the zone type.
+ */
+enum blk_zstate {
+ BLK_ZSTATE_NOT_WP = 0x00,
+ BLK_ZSTATE_EMPTY = 0x01,
+ BLK_ZSTATE_IMP_OPEN = 0x02,
+ BLK_ZSTATE_EXP_OPEN = 0x03,
+ BLK_ZSTATE_CLOSED = 0x04,
+ BLK_ZSTATE_READONLY = 0x05,
+ BLK_ZSTATE_FULL = 0x06,
+ BLK_ZSTATE_OFFLINE = 0x07,
+ BLK_ZSTATE_ACTIVE = 0x08,
+
+ BLK_ZSTATE_COND_MASK = 0x0F,
+
+ /* Conventional zone. */
+ BLK_ZFLAG_CONV = 0x80,
+ BLK_ZSTATE_FLAGS_MASK = ~BLK_ZSTATE_COND_MASK,
+};
+
+/*
+ * Lookup table and helper to convert enum blk_zstate conditions into enum
+ * blk_zone_condition values.
+ */
+static const u8 blk_zstate2zcond[] = {
+ [BLK_ZSTATE_NOT_WP] = BLK_ZONE_COND_NOT_WP,
+ [BLK_ZSTATE_EMPTY] = BLK_ZONE_COND_EMPTY,
+ [BLK_ZSTATE_IMP_OPEN] = BLK_ZONE_COND_IMP_OPEN,
+ [BLK_ZSTATE_EXP_OPEN] = BLK_ZONE_COND_EXP_OPEN,
+ [BLK_ZSTATE_CLOSED] = BLK_ZONE_COND_CLOSED,
+ [BLK_ZSTATE_READONLY] = BLK_ZONE_COND_READONLY,
+ [BLK_ZSTATE_FULL] = BLK_ZONE_COND_FULL,
+ [BLK_ZSTATE_OFFLINE] = BLK_ZONE_COND_OFFLINE,
+ [BLK_ZSTATE_ACTIVE] = BLK_ZONE_COND_ACTIVE,
+};
+
+static inline enum blk_zone_cond blk_zstate_to_zone_cond(enum blk_zstate zs)
+{
+ u8 idx = zs & BLK_ZSTATE_COND_MASK;
+
+ if (WARN_ON_ONCE(idx >= ARRAY_SIZE(blk_zstate2zcond)))
+ return 0;
+
+ return blk_zstate2zcond[idx];
+}
+
+/*
+ * Lookup table and helper to convert an enum blk_zone_condition into an enum
+ * blk_zstate condition value. To keep the lookup table small, the
+ * BLK_ZONE_COND_ACTIVE condition is not added and handled separately.
+ */
+static const u8 blk_zcond2zstate[] = {
+ [BLK_ZONE_COND_NOT_WP] = BLK_ZSTATE_NOT_WP,
+ [BLK_ZONE_COND_EMPTY] = BLK_ZSTATE_EMPTY,
+ [BLK_ZONE_COND_IMP_OPEN] = BLK_ZSTATE_ACTIVE,
+ [BLK_ZONE_COND_EXP_OPEN] = BLK_ZSTATE_ACTIVE,
+ [BLK_ZONE_COND_CLOSED] = BLK_ZSTATE_ACTIVE,
+ [BLK_ZONE_COND_READONLY] = BLK_ZSTATE_READONLY,
+ [BLK_ZONE_COND_FULL] = BLK_ZSTATE_FULL,
+ [BLK_ZONE_COND_OFFLINE] = BLK_ZSTATE_OFFLINE,
+};
+
+static inline enum blk_zstate blk_zone_cond_to_zstate(enum blk_zone_cond cond)
+{
+ if (cond == BLK_ZONE_COND_ACTIVE)
+ return BLK_ZSTATE_ACTIVE;
+
+ if (WARN_ON_ONCE(cond >= ARRAY_SIZE(blk_zcond2zstate)))
+ return 0;
+
+ return blk_zcond2zstate[cond];
+}
+
+/*
+ * Combine an enum blk_zone_condition and zone flags into a zones_state array
+ * entry.
+ */
+static inline void blk_zstate_set(u8 *zones_state, unsigned int zno,
+ enum blk_zone_cond cond, u8 flags)
+{
+ if (zones_state)
+ zones_state[zno] = flags | blk_zone_cond_to_zstate(cond);
+}
+
+static inline u8 blk_zstate_flags(enum blk_zstate zs)
+{
+ return zs & BLK_ZSTATE_FLAGS_MASK;
+}
+
+static inline bool blk_zstate_is_conv(enum blk_zstate zs)
+{
+ return blk_zstate_flags(zs) & BLK_ZFLAG_CONV;
+}
+
/*
* Per-zone write plug.
* @node: hlist_node structure for managing the plug using a hash table.
@@ -135,55 +232,64 @@ const char *blk_zone_cond_str(enum blk_zone_cond zone_cond)
}
EXPORT_SYMBOL_GPL(blk_zone_cond_str);
-static void blk_zone_set_cond(u8 *zones_cond, unsigned int zno,
- enum blk_zone_cond cond)
+static void disk_zone_set_cond(struct gendisk *disk, sector_t sector,
+ enum blk_zone_cond cond)
{
- if (!zones_cond)
- return;
+ unsigned int zno = disk_zone_no(disk, sector);
+ u8 *zones_state;
- switch (cond) {
- case BLK_ZONE_COND_IMP_OPEN:
- case BLK_ZONE_COND_EXP_OPEN:
- case BLK_ZONE_COND_CLOSED:
- zones_cond[zno] = BLK_ZONE_COND_ACTIVE;
- return;
- case BLK_ZONE_COND_NOT_WP:
- case BLK_ZONE_COND_EMPTY:
- case BLK_ZONE_COND_FULL:
- case BLK_ZONE_COND_OFFLINE:
- case BLK_ZONE_COND_READONLY:
- default:
- zones_cond[zno] = cond;
- return;
- }
+ rcu_read_lock();
+ zones_state = rcu_dereference(disk->zones_state);
+ if (likely(zones_state && zno < disk->nr_zones))
+ blk_zstate_set(zones_state, zno, cond,
+ blk_zstate_flags(zones_state[zno]));
+ rcu_read_unlock();
}
-static void disk_zone_set_cond(struct gendisk *disk, sector_t sector,
- enum blk_zone_cond cond)
+static inline u8 disk_zone_get_state(struct gendisk *disk, sector_t sector)
{
- u8 *zones_cond;
+ unsigned int zno = disk_zone_no(disk, sector);
+ u8 *zones_state, zs;
rcu_read_lock();
- zones_cond = rcu_dereference(disk->zones_cond);
- if (zones_cond) {
- unsigned int zno = disk_zone_no(disk, sector);
-
- /*
- * The condition of a conventional, readonly and offline zones
- * never changes, so do nothing if the target zone is in one of
- * these conditions.
- */
- switch (zones_cond[zno]) {
- case BLK_ZONE_COND_NOT_WP:
- case BLK_ZONE_COND_READONLY:
- case BLK_ZONE_COND_OFFLINE:
- break;
- default:
- blk_zone_set_cond(zones_cond, zno, cond);
- break;
- }
- }
+ zones_state = rcu_dereference(disk->zones_state);
+ if (likely(zones_state && zno < disk->nr_zones))
+ zs = zones_state[zno];
+ else
+ zs = 0;
rcu_read_unlock();
+
+ return zs;
+}
+
+static enum blk_zone_cond disk_zone_get_cond(struct gendisk *disk,
+ sector_t sector)
+{
+ u8 zs = disk_zone_get_state(disk, sector);
+
+ return blk_zstate_to_zone_cond(zs);
+}
+
+static inline bool
+disk_zone_cond_is_offline_or_readonly(enum blk_zone_cond cond)
+{
+ return cond == BLK_ZONE_COND_READONLY ||
+ cond == BLK_ZONE_COND_OFFLINE;
+}
+
+static inline bool disk_zone_is_offline_or_readonly(struct gendisk *disk,
+ sector_t sector)
+{
+ enum blk_zone_cond cond = disk_zone_get_cond(disk, sector);
+
+ return disk_zone_cond_is_offline_or_readonly(cond);
+}
+
+static bool disk_zone_is_seq(struct gendisk *disk, sector_t sector)
+{
+ u8 zs = disk_zone_get_state(disk, sector);
+
+ return !blk_zstate_is_conv(zs);
}
/**
@@ -195,23 +301,36 @@ static void disk_zone_set_cond(struct gendisk *disk, sector_t sector,
*/
bool bdev_zone_is_seq(struct block_device *bdev, sector_t sector)
{
- struct gendisk *disk = bdev->bd_disk;
- unsigned int zno = disk_zone_no(disk, sector);
- bool is_seq = false;
- u8 *zones_cond;
+ if (!bdev_is_zoned(bdev))
+ return false;
+
+ return disk_zone_is_seq(bdev->bd_disk, sector);
+}
+EXPORT_SYMBOL_GPL(bdev_zone_is_seq);
+
+/**
+ * bdev_check_zone_mgmt - check if a sector belongs to a valid sequential zone
+ * @bdev: block device to check
+ * @sector: sector number
+ *
+ * Check if @sector on @bdev is contained in a sequential write required zone
+ * that is not offline nor read-only.
+ */
+bool bdev_check_zone_mgmt(struct block_device *bdev, sector_t sector)
+{
+ enum blk_zone_cond cond;
+ u8 zs;
if (!bdev_is_zoned(bdev))
return false;
- rcu_read_lock();
- zones_cond = rcu_dereference(disk->zones_cond);
- if (zones_cond && zno < disk->nr_zones)
- is_seq = zones_cond[zno] != BLK_ZONE_COND_NOT_WP;
- rcu_read_unlock();
+ zs = disk_zone_get_state(bdev->bd_disk, sector);
+ if (blk_zstate_is_conv(zs))
+ return false;
- return is_seq;
+ cond = blk_zstate_to_zone_cond(zs);
+ return !disk_zone_cond_is_offline_or_readonly(cond);
}
-EXPORT_SYMBOL_GPL(bdev_zone_is_seq);
/*
* Zone report arguments for block device drivers report_zones operation.
@@ -500,12 +619,17 @@ static bool disk_zone_wplug_is_full(struct gendisk *disk,
return zwplug->wp_offset >= disk->last_zone_capacity;
}
+static bool disk_zone_wplug_is_offline_or_readonly(struct blk_zone_wplug *zwplug)
+{
+ return disk_zone_cond_is_offline_or_readonly(zwplug->cond);
+}
+
static bool disk_insert_zone_wplug(struct gendisk *disk,
struct blk_zone_wplug *zwplug)
{
struct blk_zone_wplug *zwplg;
unsigned long flags;
- u8 *zones_cond;
+ u8 *zones_state;
unsigned int idx =
hash_32(zwplug->zone_no, disk->zone_wplugs_hash_bits);
@@ -524,15 +648,16 @@ static bool disk_insert_zone_wplug(struct gendisk *disk,
}
/*
- * Set the zone condition: if we do not yet have a zones_cond array
+ * Set the zone condition: if we do not yet have a zones_state array
* attached to the disk, then this is a zone write plug insert from the
* first call to blk_revalidate_disk_zones(), in which case the zone is
* necessarilly in the active condition.
*/
- zones_cond = rcu_dereference_check(disk->zones_cond,
+ zones_state = rcu_dereference_check(disk->zones_state,
lockdep_is_held(&disk->zone_wplugs_hash_lock));
- if (zones_cond)
- zwplug->cond = zones_cond[zwplug->zone_no];
+ if (zones_state)
+ zwplug->cond =
+ blk_zstate_to_zone_cond(zones_state[zwplug->zone_no]);
else
zwplug->cond = BLK_ZONE_COND_ACTIVE;
@@ -574,6 +699,26 @@ static inline struct blk_zone_wplug *disk_get_zone_wplug(struct gendisk *disk,
return disk_get_hashed_zone_wplug(disk, sector);
}
+static void disk_for_all_zone_wplugs(struct gendisk *disk,
+ void (*actor)(struct blk_zone_wplug *,
+ void *),
+ void *data)
+{
+ struct blk_zone_wplug *zwplug;
+ unsigned int i;
+
+ if (!disk->zone_wplugs_hash)
+ return;
+
+ rcu_read_lock();
+ for (i = 0; i < disk_zone_wplugs_hash_size(disk); i++) {
+ hlist_for_each_entry_rcu(zwplug, &disk->zone_wplugs_hash[i],
+ node)
+ actor(zwplug, data);
+ }
+ rcu_read_unlock();
+}
+
static void disk_free_zone_wplug_rcu(struct rcu_head *rcu_head)
{
struct blk_zone_wplug *zwplug =
@@ -592,9 +737,9 @@ static void disk_free_zone_wplug(struct blk_zone_wplug *zwplug)
WARN_ON_ONCE(!bio_list_empty(&zwplug->bio_list));
spin_lock_irqsave(&disk->zone_wplugs_hash_lock, flags);
- blk_zone_set_cond(rcu_dereference_check(disk->zones_cond,
+ blk_zstate_set(rcu_dereference_check(disk->zones_state,
lockdep_is_held(&disk->zone_wplugs_hash_lock)),
- zwplug->zone_no, zwplug->cond);
+ zwplug->zone_no, zwplug->cond, 0);
hlist_del_init_rcu(&zwplug->node);
atomic_dec(&disk->nr_zone_wplugs);
spin_unlock_irqrestore(&disk->zone_wplugs_hash_lock, flags);
@@ -608,6 +753,53 @@ static inline void disk_put_zone_wplug(struct blk_zone_wplug *zwplug)
disk_free_zone_wplug(zwplug);
}
+static inline void blk_zone_wplug_bio_io_error(struct blk_zone_wplug *zwplug,
+ struct bio *bio)
+{
+ struct request_queue *q = zwplug->disk->queue;
+
+ bio_clear_flag(bio, BIO_ZONE_WRITE_PLUGGING);
+ bio_io_error(bio);
+ disk_put_zone_wplug(zwplug);
+ /* Drop the reference taken by disk_zone_wplug_add_bio(). */
+ blk_queue_exit(q);
+}
+
+/*
+ * Abort (fail) all plugged BIOs of a zone write plug.
+ */
+static void disk_zone_wplug_abort(struct blk_zone_wplug *zwplug)
+{
+ struct gendisk *disk = zwplug->disk;
+ struct bio *bio;
+
+ lockdep_assert_held(&zwplug->lock);
+
+ if (bio_list_empty(&zwplug->bio_list))
+ return;
+
+ pr_warn_ratelimited("%s: zone %u: Aborting plugged BIOs\n",
+ zwplug->disk->disk_name, zwplug->zone_no);
+ while ((bio = bio_list_pop(&zwplug->bio_list)))
+ blk_zone_wplug_bio_io_error(zwplug, bio);
+
+ zwplug->flags &= ~BLK_ZONE_WPLUG_PLUGGED;
+
+ /*
+ * If we are using the per disk zone write plugs worker thread, remove
+ * the zone write plug from the work list and drop the reference we
+ * took when the zone write plug was added to that list.
+ */
+ if (blk_queue_zoned_qd1_writes(disk->queue)) {
+ spin_lock(&disk->zone_wplugs_list_lock);
+ if (!list_empty(&zwplug->entry)) {
+ list_del_init(&zwplug->entry);
+ disk_put_zone_wplug(zwplug);
+ }
+ spin_unlock(&disk->zone_wplugs_list_lock);
+ }
+}
+
/*
* Flag the zone write plug as dead and drop the initial reference we got when
* the zone write plug was added to the hash table. The zone write plug will be
@@ -625,6 +817,12 @@ static void disk_mark_zone_wplug_dead(struct blk_zone_wplug *zwplug)
static inline bool disk_check_zone_wplug_dead(struct blk_zone_wplug *zwplug)
{
+ if (disk_zone_wplug_is_offline_or_readonly(zwplug)) {
+ disk_zone_wplug_abort(zwplug);
+ disk_mark_zone_wplug_dead(zwplug);
+ return true;
+ }
+
if (!(zwplug->flags & BLK_ZONE_WPLUG_DEAD))
return false;
@@ -708,53 +906,6 @@ static struct blk_zone_wplug *disk_get_or_alloc_zone_wplug(struct gendisk *disk,
return zwplug;
}
-static inline void blk_zone_wplug_bio_io_error(struct blk_zone_wplug *zwplug,
- struct bio *bio)
-{
- struct request_queue *q = zwplug->disk->queue;
-
- bio_clear_flag(bio, BIO_ZONE_WRITE_PLUGGING);
- bio_io_error(bio);
- disk_put_zone_wplug(zwplug);
- /* Drop the reference taken by disk_zone_wplug_add_bio(). */
- blk_queue_exit(q);
-}
-
-/*
- * Abort (fail) all plugged BIOs of a zone write plug.
- */
-static void disk_zone_wplug_abort(struct blk_zone_wplug *zwplug)
-{
- struct gendisk *disk = zwplug->disk;
- struct bio *bio;
-
- lockdep_assert_held(&zwplug->lock);
-
- if (bio_list_empty(&zwplug->bio_list))
- return;
-
- pr_warn_ratelimited("%s: zone %u: Aborting plugged BIOs\n",
- zwplug->disk->disk_name, zwplug->zone_no);
- while ((bio = bio_list_pop(&zwplug->bio_list)))
- blk_zone_wplug_bio_io_error(zwplug, bio);
-
- zwplug->flags &= ~BLK_ZONE_WPLUG_PLUGGED;
-
- /*
- * If we are using the per disk zone write plugs worker thread, remove
- * the zone write plug from the work list and drop the reference we
- * took when the zone write plug was added to that list.
- */
- if (blk_queue_zoned_qd1_writes(disk->queue)) {
- spin_lock(&disk->zone_wplugs_list_lock);
- if (!list_empty(&zwplug->entry)) {
- list_del_init(&zwplug->entry);
- disk_put_zone_wplug(zwplug);
- }
- spin_unlock(&disk->zone_wplugs_list_lock);
- }
-}
-
/*
* Update a zone write plug condition based on the write pointer offset.
*/
@@ -785,8 +936,10 @@ static void disk_zone_wplug_set_wp_offset(struct gendisk *disk,
/* Update the zone write pointer and abort all plugged BIOs. */
zwplug->flags &= ~BLK_ZONE_WPLUG_NEED_WP_UPDATE;
- zwplug->wp_offset = wp_offset;
- disk_zone_wplug_update_cond(disk, zwplug);
+ if (!disk_zone_wplug_is_offline_or_readonly(zwplug)) {
+ zwplug->wp_offset = wp_offset;
+ disk_zone_wplug_update_cond(disk, zwplug);
+ }
disk_zone_wplug_abort(zwplug);
if (!zwplug->wp_offset || disk_zone_wplug_is_full(disk, zwplug))
@@ -816,8 +969,8 @@ static unsigned int blk_zone_wp_offset(struct blk_zone *zone)
}
}
-static unsigned int disk_zone_wplug_sync_wp_offset(struct gendisk *disk,
- struct blk_zone *zone)
+static unsigned int disk_zone_wplug_sync_state(struct gendisk *disk,
+ struct blk_zone *zone)
{
struct blk_zone_wplug *zwplug;
unsigned int wp_offset = blk_zone_wp_offset(zone);
@@ -829,6 +982,10 @@ static unsigned int disk_zone_wplug_sync_wp_offset(struct gendisk *disk,
spin_lock_irqsave(&zwplug->lock, flags);
if (zwplug->flags & BLK_ZONE_WPLUG_NEED_WP_UPDATE)
disk_zone_wplug_set_wp_offset(disk, zwplug, wp_offset);
+ if (disk_zone_cond_is_offline_or_readonly(zone->cond)) {
+ zwplug->cond = zone->cond;
+ disk_mark_zone_wplug_dead(zwplug);
+ }
spin_unlock_irqrestore(&zwplug->lock, flags);
disk_put_zone_wplug(zwplug);
}
@@ -871,7 +1028,7 @@ int disk_report_zone(struct gendisk *disk, struct blk_zone *zone,
}
if (disk->zone_wplugs_hash)
- disk_zone_wplug_sync_wp_offset(disk, zone);
+ disk_zone_wplug_sync_state(disk, zone);
if (args && args->cb)
return args->cb(zone, idx, args->data);
@@ -940,7 +1097,7 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
sector_t zone_sectors = bdev_zone_sectors(bdev);
struct blk_zone_wplug *zwplug;
unsigned long flags;
- u8 *zones_cond;
+ u8 *zones_state, zs;
if (!bdev_is_zoned(bdev))
return -EOPNOTSUPP;
@@ -955,12 +1112,18 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
return blkdev_report_zone_fallback(bdev, sector, zone);
rcu_read_lock();
- zones_cond = rcu_dereference(disk->zones_cond);
- if (!disk->zone_wplugs_hash || !zones_cond) {
+ zones_state = rcu_dereference(disk->zones_state);
+ if (!disk->zone_wplugs_hash || !zones_state) {
rcu_read_unlock();
return blkdev_report_zone_fallback(bdev, sector, zone);
}
- zone->cond = zones_cond[disk_zone_no(disk, sector)];
+
+ zs = zones_state[disk_zone_no(disk, sector)];
+ zone->cond = blk_zstate_to_zone_cond(zs);
+ if (blk_zstate_is_conv(zs))
+ zone->type = BLK_ZONE_TYPE_CONVENTIONAL;
+ else
+ zone->type = BLK_ZONE_TYPE_SEQWRITE_REQ;
rcu_read_unlock();
zone->start = sector;
@@ -970,8 +1133,7 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
* If this is a conventional zone, we do not have a zone write plug and
* can report the zone immediately.
*/
- if (zone->cond == BLK_ZONE_COND_NOT_WP) {
- zone->type = BLK_ZONE_TYPE_CONVENTIONAL;
+ if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL) {
zone->capacity = zone_sectors;
zone->wp = ULLONG_MAX;
return 0;
@@ -982,14 +1144,12 @@ int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
* offline, only set the zone write pointer to an invalid value and
* report the zone.
*/
- zone->type = BLK_ZONE_TYPE_SEQWRITE_REQ;
if (disk_zone_is_last(disk, zone))
zone->capacity = disk->last_zone_capacity;
else
zone->capacity = disk->zone_capacity;
- if (zone->cond == BLK_ZONE_COND_READONLY ||
- zone->cond == BLK_ZONE_COND_OFFLINE) {
+ if (disk_zone_cond_is_offline_or_readonly(zone->cond)) {
zone->wp = ULLONG_MAX;
return 0;
}
@@ -1108,34 +1268,32 @@ static void blk_zone_reset_bio_endio(struct bio *bio)
}
}
+static void disk_zone_wplug_reset_wp(struct blk_zone_wplug *zwplug, void *data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&zwplug->lock, flags);
+ disk_zone_wplug_set_wp_offset(zwplug->disk, zwplug, 0);
+ spin_unlock_irqrestore(&zwplug->lock, flags);
+}
+
static void blk_zone_reset_all_bio_endio(struct bio *bio)
{
struct gendisk *disk = bio->bi_bdev->bd_disk;
- sector_t capacity = get_capacity(disk);
- struct blk_zone_wplug *zwplug;
- unsigned long flags;
sector_t sector;
- unsigned int i;
- if (atomic_read(&disk->nr_zone_wplugs)) {
- /* Update the condition of all zone write plugs. */
- rcu_read_lock();
- for (i = 0; i < disk_zone_wplugs_hash_size(disk); i++) {
- hlist_for_each_entry_rcu(zwplug,
- &disk->zone_wplugs_hash[i],
- node) {
- spin_lock_irqsave(&zwplug->lock, flags);
- disk_zone_wplug_set_wp_offset(disk, zwplug, 0);
- spin_unlock_irqrestore(&zwplug->lock, flags);
- }
- }
- rcu_read_unlock();
- }
+ /* Update the condition of all zone write plugs. */
+ if (atomic_read(&disk->nr_zone_wplugs))
+ disk_for_all_zone_wplugs(disk, disk_zone_wplug_reset_wp, NULL);
/* Update the cached zone conditions. */
- for (sector = 0; sector < capacity;
- sector += bdev_zone_sectors(bio->bi_bdev))
+ for (sector = 0; sector < get_capacity(disk);
+ sector += bdev_zone_sectors(bio->bi_bdev)) {
+ if (!disk_zone_is_seq(disk, sector) ||
+ disk_zone_is_offline_or_readonly(disk, sector))
+ continue;
disk_zone_set_cond(disk, sector, BLK_ZONE_COND_EMPTY);
+ }
clear_bit(GD_ZONE_APPEND_USED, &disk->state);
}
@@ -1381,11 +1539,12 @@ static bool blk_zone_wplug_prepare_bio(struct blk_zone_wplug *zwplug,
return false;
/*
- * Check that the user is not attempting to write to a full zone.
- * We know such BIO will fail, and that would potentially overflow our
- * write pointer offset beyond the end of the zone.
+ * Check that the user is not attempting to write to a full, read-only
+ * or offline zone. We know such BIOs will fail, so there is no point
+ * in issuing them.
*/
- if (disk_zone_wplug_is_full(disk, zwplug))
+ if (disk_zone_wplug_is_full(disk, zwplug) ||
+ disk_zone_wplug_is_offline_or_readonly(zwplug))
return false;
if (bio_op(bio) == REQ_OP_ZONE_APPEND) {
@@ -1442,7 +1601,7 @@ static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs)
}
/* Conventional zones do not need write plugging. */
- if (!bdev_zone_is_seq(bio->bi_bdev, sector)) {
+ if (!disk_zone_is_seq(disk, sector)) {
/* Zone append to conventional zones is not allowed. */
if (bio_op(bio) == REQ_OP_ZONE_APPEND) {
bio_io_error(bio);
@@ -1854,12 +2013,20 @@ static int disk_zone_wplugs_worker(void *data)
void disk_init_zone_resources(struct gendisk *disk)
{
+ atomic_set(&disk->nr_zone_wplugs, 0);
spin_lock_init(&disk->zone_wplugs_hash_lock);
spin_lock_init(&disk->zone_wplugs_list_lock);
INIT_LIST_HEAD(&disk->zone_wplugs_list);
init_completion(&disk->zone_wplugs_worker_bio_done);
}
+static unsigned int disk_get_nr_zones(struct gendisk *disk)
+{
+ struct queue_limits *lim = &disk->queue->limits;
+
+ return DIV_ROUND_UP_ULL(get_capacity(disk), lim->chunk_sectors);
+}
+
/*
* For the size of a disk zone write plug hash table, use the size of the
* zone write plug mempool, which is the maximum of the disk open zones and
@@ -1869,13 +2036,21 @@ void disk_init_zone_resources(struct gendisk *disk)
#define BLK_ZONE_WPLUG_MAX_HASH_BITS 9
#define BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE 128
-static int disk_alloc_zone_resources(struct gendisk *disk,
- unsigned int pool_size)
+static int disk_alloc_zone_resources(struct gendisk *disk)
{
- unsigned int i;
+ struct queue_limits *lim = &disk->queue->limits;
+ unsigned int nr_zones = disk_get_nr_zones(disk);
+ unsigned int pool_size, i;
int ret = -ENOMEM;
- atomic_set(&disk->nr_zone_wplugs, 0);
+ /*
+ * If the device has no limit on the maximum number of open and active
+ * zones, use BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE.
+ */
+ pool_size = max(lim->max_open_zones, lim->max_active_zones);
+ if (!pool_size)
+ pool_size = min(BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE, nr_zones);
+
disk->zone_wplugs_hash_bits =
min(ilog2(pool_size) + 1, BLK_ZONE_WPLUG_MAX_HASH_BITS);
@@ -1893,21 +2068,6 @@ static int disk_alloc_zone_resources(struct gendisk *disk,
if (!disk->zone_wplugs_pool)
goto free_hash;
- /*
- * We may already have a zone write plug workqueue as this function may
- * be called after disk_free_zone_resources(), which does not destroy
- * the workqueue (the zone write plugs workqueue is destroyed at
- * disk_release() time).
- */
- if (!disk->zone_wplugs_wq) {
- disk->zone_wplugs_wq =
- alloc_workqueue("%s_zwplugs",
- WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,
- pool_size, disk->disk_name);
- if (!disk->zone_wplugs_wq)
- goto destroy_pool;
- }
-
disk->zone_wplugs_worker =
kthread_create(disk_zone_wplugs_worker, disk,
"%s_zwplugs_worker", disk->disk_name);
@@ -1918,8 +2078,18 @@ static int disk_alloc_zone_resources(struct gendisk *disk,
}
wake_up_process(disk->zone_wplugs_worker);
+ disk->zone_wplugs_wq =
+ alloc_workqueue("%s_zwplugs",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,
+ pool_size, disk->disk_name);
+ if (!disk->zone_wplugs_wq)
+ goto stop_worker;
+
return 0;
+stop_worker:
+ kthread_stop(disk->zone_wplugs_worker);
+ disk->zone_wplugs_worker = NULL;
destroy_pool:
mempool_destroy(disk->zone_wplugs_pool);
disk->zone_wplugs_pool = NULL;
@@ -1963,19 +2133,19 @@ static void disk_destroy_zone_wplugs_hash_table(struct gendisk *disk)
disk->zone_wplugs_pool = NULL;
}
-static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond)
+static void disk_set_zones_state_array(struct gendisk *disk, u8 *zones_state)
{
unsigned long flags;
spin_lock_irqsave(&disk->zone_wplugs_hash_lock, flags);
- zones_cond = rcu_replace_pointer(disk->zones_cond, zones_cond,
+ zones_state = rcu_replace_pointer(disk->zones_state, zones_state,
lockdep_is_held(&disk->zone_wplugs_hash_lock));
spin_unlock_irqrestore(&disk->zone_wplugs_hash_lock, flags);
- kfree_rcu_mightsleep(zones_cond);
+ kfree_rcu_mightsleep(zones_state);
}
-static void disk_free_zone_resources(struct gendisk *disk)
+void disk_release_zone_resources(struct gendisk *disk)
{
if (disk->zone_wplugs_worker) {
kthread_stop(disk->zone_wplugs_worker);
@@ -1983,30 +2153,22 @@ static void disk_free_zone_resources(struct gendisk *disk)
}
WARN_ON_ONCE(!list_empty(&disk->zone_wplugs_list));
- if (disk->zone_wplugs_wq)
- drain_workqueue(disk->zone_wplugs_wq);
+ if (disk->zone_wplugs_wq) {
+ destroy_workqueue(disk->zone_wplugs_wq);
+ disk->zone_wplugs_wq = NULL;
+ }
disk_destroy_zone_wplugs_hash_table(disk);
- disk_set_zones_cond_array(disk, NULL);
+ disk_set_zones_state_array(disk, NULL);
disk->zone_capacity = 0;
disk->last_zone_capacity = 0;
disk->nr_zones = 0;
}
-void disk_release_zone_resources(struct gendisk *disk)
-{
- if (disk->zone_wplugs_wq) {
- destroy_workqueue(disk->zone_wplugs_wq);
- disk->zone_wplugs_wq = NULL;
- }
-
- disk_free_zone_resources(disk);
-}
-
struct blk_revalidate_zone_args {
struct gendisk *disk;
- u8 *zones_cond;
+ u8 *zones_state;
unsigned int nr_zones;
unsigned int nr_conv_zones;
unsigned int zone_capacity;
@@ -2014,49 +2176,26 @@ struct blk_revalidate_zone_args {
sector_t sector;
};
-static int disk_revalidate_zone_resources(struct gendisk *disk,
+static int disk_init_revalidate_args(struct gendisk *disk,
struct blk_revalidate_zone_args *args)
{
- struct queue_limits *lim = &disk->queue->limits;
- unsigned int pool_size;
- int ret = 0;
-
args->disk = disk;
- args->nr_zones =
- DIV_ROUND_UP_ULL(get_capacity(disk), lim->chunk_sectors);
+ args->nr_zones = disk_get_nr_zones(disk);
/* Cached zone conditions: 1 byte per zone */
- args->zones_cond = kzalloc(args->nr_zones, GFP_NOIO);
- if (!args->zones_cond)
+ args->zones_state = kzalloc(args->nr_zones, GFP_NOIO);
+ if (!args->zones_state)
return -ENOMEM;
- if (!disk_need_zone_resources(disk))
- return 0;
-
- /*
- * If the device has no limit on the maximum number of open and active
- * zones, use BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE.
- */
- pool_size = max(lim->max_open_zones, lim->max_active_zones);
- if (!pool_size)
- pool_size =
- min(BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE, args->nr_zones);
-
- if (!disk->zone_wplugs_hash) {
- ret = disk_alloc_zone_resources(disk, pool_size);
- if (ret)
- kfree(args->zones_cond);
- }
-
- return ret;
+ return 0;
}
/*
- * Update the disk zone resources information and device queue limits.
- * The disk queue is frozen when this is executed.
+ * Revalidate and update the disk zone resources information and device queue
+ * limits.
*/
-static int disk_update_zone_resources(struct gendisk *disk,
- struct blk_revalidate_zone_args *args)
+static int disk_revalidate_zone_resources(struct gendisk *disk,
+ struct blk_revalidate_zone_args *args)
{
struct request_queue *q = disk->queue;
unsigned int nr_seq_zones;
@@ -2064,23 +2203,28 @@ static int disk_update_zone_resources(struct gendisk *disk,
struct queue_limits lim;
int ret = 0;
+ /* Make sure that the entire disk capacity has been checked. */
+ if (args->sector != get_capacity(disk)) {
+ pr_warn("%s: Missing zones from sector %llu\n",
+ disk->disk_name, args->sector);
+ return -ENODEV;
+ }
+
+ if (args->nr_conv_zones >= args->nr_zones) {
+ pr_warn("%s: Invalid number of conventional zones %u / %u\n",
+ disk->disk_name, args->nr_conv_zones, args->nr_zones);
+ return -ENODEV;
+ }
+
lim = queue_limits_start_update(q);
memflags = blk_mq_freeze_queue(q);
disk->nr_zones = args->nr_zones;
- if (args->nr_conv_zones >= disk->nr_zones) {
- queue_limits_cancel_update(q);
- pr_warn("%s: Invalid number of conventional zones %u / %u\n",
- disk->disk_name, args->nr_conv_zones, disk->nr_zones);
- ret = -ENODEV;
- goto unfreeze;
- }
-
disk->zone_capacity = args->zone_capacity;
disk->last_zone_capacity = args->last_zone_capacity;
- disk_set_zones_cond_array(disk, args->zones_cond);
- args->zones_cond = NULL;
+ disk_set_zones_state_array(disk, args->zones_state);
+ args->zones_state = NULL;
/*
* Some devices can advertise zone resource limits that are larger than
@@ -2120,48 +2264,59 @@ static int disk_update_zone_resources(struct gendisk *disk,
commit:
ret = queue_limits_commit_update(q, &lim);
-unfreeze:
blk_mq_unfreeze_queue(q, memflags);
return ret;
}
-static int blk_revalidate_zone_cond(struct blk_zone *zone, unsigned int idx,
- struct blk_revalidate_zone_args *args)
+static void disk_drop_zone_wplug(struct blk_zone_wplug *zwplug, void *data)
{
- enum blk_zone_cond cond = zone->cond;
+ unsigned long flags;
- /* Check that the zone condition is consistent with the zone type. */
- switch (cond) {
- case BLK_ZONE_COND_NOT_WP:
- if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL)
- goto invalid_condition;
- break;
- case BLK_ZONE_COND_IMP_OPEN:
- case BLK_ZONE_COND_EXP_OPEN:
- case BLK_ZONE_COND_CLOSED:
- case BLK_ZONE_COND_EMPTY:
- case BLK_ZONE_COND_FULL:
- case BLK_ZONE_COND_OFFLINE:
- case BLK_ZONE_COND_READONLY:
- if (zone->type != BLK_ZONE_TYPE_SEQWRITE_REQ)
- goto invalid_condition;
- break;
- default:
- pr_warn("%s: Invalid zone condition 0x%X\n",
- args->disk->disk_name, cond);
- return -ENODEV;
+ spin_lock_irqsave(&zwplug->lock, flags);
+ disk_zone_wplug_abort(zwplug);
+ disk_mark_zone_wplug_dead(zwplug);
+ spin_unlock_irqrestore(&zwplug->lock, flags);
+}
+
+static int disk_revalidate_capacity(struct gendisk *disk)
+{
+ struct queue_limits *lim = &disk->queue->limits;
+ sector_t zone_sectors = lim->chunk_sectors;
+ unsigned int nr_zones = disk_get_nr_zones(disk);
+ int ret = -ENODEV;
+
+ if (!get_capacity(disk))
+ goto drop_all_zwplugs;
+
+ /*
+ * Checks that the device driver indicated a valid zone size and that
+ * the max zone append limit is set.
+ */
+ if (!zone_sectors || !is_power_of_2(zone_sectors)) {
+ pr_warn("%s: Invalid non power of two zone size (%llu)\n",
+ disk->disk_name, zone_sectors);
+ goto drop_all_zwplugs;
}
- blk_zone_set_cond(args->zones_cond, idx, cond);
+ /*
+ * Check if the capacity has changed. If it did, assume that the device
+ * was reformatted and that all sequential zones are now empty. So drop
+ * all zone write plug.
+ */
+ if (disk->nr_zones && disk->nr_zones != nr_zones) {
+ pr_warn("%s: Number of zones changed (%u -> %u)\n",
+ disk->disk_name, disk->nr_zones, nr_zones);
+ ret = 0;
+ goto drop_all_zwplugs;
+ }
return 0;
-invalid_condition:
- pr_warn("%s: Invalid zone condition 0x%x for type 0x%x\n",
- args->disk->disk_name, cond, zone->type);
+drop_all_zwplugs:
+ disk_for_all_zone_wplugs(disk, disk_drop_zone_wplug, NULL);
- return -ENODEV;
+ return ret;
}
static int blk_revalidate_conv_zone(struct blk_zone *zone, unsigned int idx,
@@ -2169,12 +2324,26 @@ static int blk_revalidate_conv_zone(struct blk_zone *zone, unsigned int idx,
{
struct gendisk *disk = args->disk;
+ /* Check the zone condition. */
+ switch (zone->cond) {
+ case BLK_ZONE_COND_NOT_WP:
+ case BLK_ZONE_COND_OFFLINE:
+ case BLK_ZONE_COND_READONLY:
+ break;
+ default:
+ pr_warn("%s: Invalid conv. zone condition 0x%X at sector %llu\n",
+ disk->disk_name, zone->cond, zone->start);
+ return -ENODEV;
+ }
+
if (zone->capacity != zone->len) {
pr_warn("%s: Invalid conventional zone capacity\n",
disk->disk_name);
return -ENODEV;
}
+ blk_zstate_set(args->zones_state, idx, zone->cond, BLK_ZFLAG_CONV);
+
if (disk_zone_is_last(disk, zone))
args->last_zone_capacity = zone->capacity;
@@ -2190,6 +2359,24 @@ static int blk_revalidate_seq_zone(struct blk_zone *zone, unsigned int idx,
struct blk_zone_wplug *zwplug;
unsigned int wp_offset;
+ /* Check the zone condition. */
+ switch (zone->cond) {
+ case BLK_ZONE_COND_IMP_OPEN:
+ case BLK_ZONE_COND_EXP_OPEN:
+ case BLK_ZONE_COND_CLOSED:
+ case BLK_ZONE_COND_EMPTY:
+ case BLK_ZONE_COND_FULL:
+ case BLK_ZONE_COND_OFFLINE:
+ case BLK_ZONE_COND_READONLY:
+ break;
+ default:
+ pr_warn("%s: Invalid seq. zone condition 0x%X at sector %llu\n",
+ disk->disk_name, zone->cond, zone->start);
+ return -ENODEV;
+ }
+
+ blk_zstate_set(args->zones_state, idx, zone->cond, 0);
+
/*
* Remember the capacity of the first sequential zone and check
* if it is constant for all zones, ignoring the last zone as it can be
@@ -2214,7 +2401,7 @@ static int blk_revalidate_seq_zone(struct blk_zone *zone, unsigned int idx,
if (!disk->zone_wplugs_hash)
return 0;
- wp_offset = disk_zone_wplug_sync_wp_offset(disk, zone);
+ wp_offset = disk_zone_wplug_sync_state(disk, zone);
if (!wp_offset || wp_offset >= zone->capacity)
return 0;
@@ -2272,11 +2459,6 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
return -ENODEV;
}
- /* Check zone condition */
- ret = blk_revalidate_zone_cond(zone, idx, args);
- if (ret)
- return ret;
-
/* Check zone type */
switch (zone->type) {
case BLK_ZONE_TYPE_CONVENTIONAL:
@@ -2313,39 +2495,37 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
*/
int blk_revalidate_disk_zones(struct gendisk *disk)
{
- struct request_queue *q = disk->queue;
- sector_t zone_sectors = q->limits.chunk_sectors;
- sector_t capacity = get_capacity(disk);
struct blk_revalidate_zone_args args = { };
- unsigned int memflags, noio_flag;
struct blk_report_zones_args rep_args = {
.cb = blk_revalidate_zone_cb,
.data = &args,
};
- int ret = -ENOMEM;
+ unsigned int noio_flag;
+ int ret;
- if (WARN_ON_ONCE(!blk_queue_is_zoned(q)))
+ if (WARN_ON_ONCE(!blk_queue_is_zoned(disk->queue)))
return -EIO;
- if (!capacity)
- return -ENODEV;
+ ret = disk_revalidate_capacity(disk);
+ if (ret)
+ return ret;
/*
- * Checks that the device driver indicated a valid zone size and that
- * the max zone append limit is set.
+ * Allocate zone resources if they are needed and we have not done
+ * so yet, and initialize the revalidation arguments passed to report
+ * zones. Ensure that all memory allocations in this context are done as
+ * if GFP_NOIO was specified.
*/
- if (!zone_sectors || !is_power_of_2(zone_sectors)) {
- pr_warn("%s: Invalid non power of two zone size (%llu)\n",
- disk->disk_name, zone_sectors);
- return -ENODEV;
+ noio_flag = memalloc_noio_save();
+ if (disk_need_zone_resources(disk) && !disk->zone_wplugs_hash) {
+ ret = disk_alloc_zone_resources(disk);
+ if (ret) {
+ memalloc_noio_restore(noio_flag);
+ return ret;
+ }
}
- /*
- * Ensure that all memory allocations in this context are done as if
- * GFP_NOIO was specified.
- */
- noio_flag = memalloc_noio_save();
- ret = disk_revalidate_zone_resources(disk, &args);
+ ret = disk_init_revalidate_args(disk, &args);
if (ret) {
memalloc_noio_restore(noio_flag);
return ret;
@@ -2359,32 +2539,18 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
memalloc_noio_restore(noio_flag);
if (ret <= 0)
- goto free_resources;
+ goto free_args;
- /*
- * If zones where reported, make sure that the entire disk capacity
- * has been checked.
- */
- if (args.sector != capacity) {
- pr_warn("%s: Missing zones from sector %llu\n",
- disk->disk_name, args.sector);
- ret = -ENODEV;
- goto free_resources;
- }
-
- ret = disk_update_zone_resources(disk, &args);
+ ret = disk_revalidate_zone_resources(disk, &args);
if (ret)
- goto free_resources;
+ goto free_args;
return 0;
-free_resources:
+free_args:
pr_warn("%s: failed to revalidate zones\n", disk->disk_name);
- kfree(args.zones_cond);
- memflags = blk_mq_freeze_queue(q);
- disk_free_zone_resources(disk);
- blk_mq_unfreeze_queue(q, memflags);
+ kfree(args.zones_state);
return ret;
}
@@ -2435,8 +2601,9 @@ EXPORT_SYMBOL_GPL(blk_zone_issue_zeroout);
#ifdef CONFIG_BLK_DEBUG_FS
static void queue_zone_wplug_show(struct blk_zone_wplug *zwplug,
- struct seq_file *m)
+ void *data)
{
+ struct seq_file *m = data;
unsigned int zwp_wp_offset, zwp_flags;
unsigned int zwp_zone_no, zwp_ref;
unsigned int zwp_bio_list_size;
@@ -2461,19 +2628,8 @@ static void queue_zone_wplug_show(struct blk_zone_wplug *zwplug,
int queue_zone_wplugs_show(void *data, struct seq_file *m)
{
struct request_queue *q = data;
- struct gendisk *disk = q->disk;
- struct blk_zone_wplug *zwplug;
- unsigned int i;
-
- if (!disk->zone_wplugs_hash)
- return 0;
- rcu_read_lock();
- for (i = 0; i < disk_zone_wplugs_hash_size(disk); i++)
- hlist_for_each_entry_rcu(zwplug, &disk->zone_wplugs_hash[i],
- node)
- queue_zone_wplug_show(zwplug, m);
- rcu_read_unlock();
+ disk_for_all_zone_wplugs(q->disk, queue_zone_wplug_show, m);
return 0;
}
diff --git a/block/blk.h b/block/blk.h
index 50abfd9328861..cfe8d4185e25a 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -577,6 +577,7 @@ int blkdev_report_zones_ioctl(struct block_device *bdev, unsigned int cmd,
unsigned long arg);
int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long arg);
+bool bdev_check_zone_mgmt(struct block_device *bdev, sector_t sector);
#else /* CONFIG_BLK_DEV_ZONED */
static inline void disk_init_zone_resources(struct gendisk *disk)
{
@@ -619,6 +620,11 @@ static inline int blkdev_zone_mgmt_ioctl(struct block_device *bdev,
{
return -ENOTTY;
}
+static inline bool bdev_check_zone_mgmt(struct block_device *bdev,
+ sector_t sector)
+{
+ return false;
+}
#endif /* CONFIG_BLK_DEV_ZONED */
struct block_device *bdev_alloc(struct gendisk *disk, u8 partno);
diff --git a/block/genhd.c b/block/genhd.c
index e8ce0cabf392c..34a64077ba02c 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -447,6 +447,13 @@ static int __add_disk(struct device *parent, struct gendisk *disk,
bdev_set_flag(disk->part0, BD_HAS_SUBMIT_BIO);
}
+ /*
+ * We do not support partitions with zoned block devices, so do not try
+ * to scan the partitions table.
+ */
+ if (blk_queue_is_zoned(disk->queue))
+ disk->flags |= GENHD_FL_NO_PART;
+
/*
* If the driver provides an explicit major number it also must provide
* the number of minors numbers supported, and those will be used to
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 20cb8ed7d987a..4fa00757527d7 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -200,7 +200,7 @@ struct gendisk {
unsigned int nr_zones;
unsigned int zone_capacity;
unsigned int last_zone_capacity;
- u8 __rcu *zones_cond;
+ u8 __rcu *zones_state;
unsigned int zone_wplugs_hash_bits;
atomic_t nr_zone_wplugs;
spinlock_t zone_wplugs_hash_lock;
|
| 3/2 | 2026/08/06 19:46 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17753 output: 23 thoughts: 540 Thoughts: **Analyzing the Zoned Block Device Patch for Fuzzing** Okay, so I've got this patch – it's touching the block layer, specifically the zoned block device implementation. We're looking at `block/blk-zoned.c`, `block/blk-core.c`, `block/blk.h`, `block/genhd.c`, and `include/linux/blkdev.h`. Straight away, I see they're introducing `enum blk_zstate` and changing how zone states are tracked. Instead of `zones_cond`, we're now dealing with `zones_state`. This has a huge functional impact. This is a good sign for potential fuzzing targets. Specifically, it looks like `submit_bio_noacct` now checks with `bdev_check_zone_mgmt`. That one is definitely worth a closer look. They're basically making sure zone management operations are only allowed on sequential zones that are online and not read-only. We also need to keep an eye on how `__add_disk` is modified in `block/genhd.c` to set `GENHD_FL_NO_PART` for zoned block devices. So, I'm thinking the core of the change revolves around these new checks and state management. Based on that, I've identified several functions that should be prime fuzzing candidates: * `bdev_check_zone_mgmt` (especially given it's now exported and called from `submit_bio_noacct`) * `disk_zone_set_cond` * `disk_zone_get_state` * `disk_zone_get_cond` * `disk_zone_is_offline_or_readonly` * `disk_zone_is_seq` * `disk_insert_zone_wplug` * `disk_zone_wplug_abort` * `disk_zone_wplug_sync_state` * `blkdev_get_zone_info` * `blk_zone_reset_all_bio_endio` * `blk_zone_wplug_prepare_bio` * `blk_zone_wplug_handle_write` * `disk_alloc_zone_resources` * `disk_revalidate_zone_resources` * `blk_revalidate_disk_zones` I'm zeroing in on a few functions which are particularly interesting: * `bdev_check_zone_mgmt` (since it's a boundary exposed to the submit path). * `blk_revalidate_disk_zones` (likely involved in initial disk setup or zone revalidation) * `blkdev_get_zone_info` (if an API, and commonly used it may be valuable to fuzz). * `disk_zone_wplug_abort` (error recovery). Now, let's verify the build configuration. We need to confirm that `CONFIG_BLK_DEV_ZONED` is actually enabled. I'll quickly check that with `git grep` or `codesearch-dir-index` to make sure we're dealing with a build that's relevant. |
| 4/2 | 2026/08/06 19:46 | tool | grepper |
0mArgs:{
"Expression": "config BLK_DEV_ZONED"
}Results: {
"Output": "block/Kconfig=77=config BLK_DEV_WRITE_MOUNTED\n--\nblock/Kconfig-96-\nblock/Kconfig:97:config BLK_DEV_ZONED\nblock/Kconfig-98-\tbool \"Zoned block device support\"\n--\ndrivers/block/Kconfig=354=source \"drivers/block/rnbd/Kconfig\"\ndrivers/block/Kconfig-355-\ndrivers/block/Kconfig:356:config BLK_DEV_ZONED_LOOP\ndrivers/block/Kconfig-357-\ttristate \"Zoned loopback device support\"\n"
} |
| 5/2 | 2026/08/06 19:46 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19187 output: 25 thoughts: 52 Thoughts: Okay, let's see, my initial focus is on `BLK_DEV_ZONED`. That's the key configuration setting for zoned block device support, naturally. I should probably quickly verify that there aren't any other related configs I need to be aware of, just to be thorough. Now, thinking more broadly, I wonder...`BLK_DEV_ZONED_LOOP` could be useful, especially for creating a virtual environment to test things without needing real hardware, that might be useful. |
| 6/2 | 2026/08/06 19:46 | tool | grepper |
0mArgs:{
"Expression": "config BLK_DEV_ZONED_LOOP"
}Results: {
"Output": "drivers/block/Kconfig=354=source \"drivers/block/rnbd/Kconfig\"\ndrivers/block/Kconfig-355-\ndrivers/block/Kconfig:356:config BLK_DEV_ZONED_LOOP\ndrivers/block/Kconfig-357-\ttristate \"Zoned loopback device support\"\n"
} |
| 7/2 | 2026/08/06 19:46 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19466 output: 34 thoughts: 19 Thoughts: Here's my analysis of the `BLK_DEV_ZONED_LOOP` kernel configuration option: **Diving into BLK_DEV_ZONED_LOOP Dependencies** Okay, so I need to figure out what other kernel features and modules are prerequisites or are related to `BLK_DEV_ZONED_LOOP`. I'm thinking I'll need to dig into the kernel source and configuration files to trace this dependency tree. Let's start by looking for any immediate dependencies defined in the Kconfig files. This usually involves searching for `depends on` statements related to this option. I’ll be watching out for things like storage stack components, block device drivers, or specific filesystem features. It could also depend on some core infrastructure like the loop device itself, obviously. It's also possible that it requires enabling certain kernel architectures. I also will want to see which other options are included, as these often imply usage of the option, though not explicit dependency. I also will make notes, as well as test my current knowledge, so I can see which I need to refresh on. I'll need to keep an eye out for any potentially conflicting options or implicit assumptions that might influence its behavior. |
| 8/2 | 2026/08/06 19:46 | tool | read-file |
0mArgs:{
"File": "drivers/block/Kconfig",
"FirstLine": 350,
"LineCount": 20
}Results: {
"Contents": " 350:\t\t Say N if you don't want to support legacy command opcode. It is\n 351:\t\t suggested to enable N if your application(ublk server) switches to\n 352:\t\t ioctl command encoding.\n 353:\t\n 354:\tsource \"drivers/block/rnbd/Kconfig\"\n 355:\t\n 356:\tconfig BLK_DEV_ZONED_LOOP\n 357:\t\ttristate \"Zoned loopback device support\"\n 358:\t\tdepends on BLK_DEV_ZONED\n 359:\t\thelp\n 360:\t\t Saying Y here will allow you to use create a zoned block device using\n 361:\t\t regular files for zones (one file per zones). This is useful to test\n 362:\t\t file systems, device mapper and applications that support zoned block\n 363:\t\t devices. To create a zoned loop device, no user utility is needed, a\n 364:\t\t zoned loop device can be created (or re-started) using a command\n 365:\t\t like:\n 366:\t\n 367:\t\t echo \"add id=0,zone_size_mb=256,capacity_mb=16384,conv_zones=11\" \u003e \\\n 368:\t\t\t/dev/zloop-control\n 369:\t\n"
} |
| 9/2 | 2026/08/06 19:46 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20132 output: 23 thoughts: 57 Thoughts: Okay, let me process this… **Zoned Block Device Confusion - A Quick Dive** Hold on a second. *BLK_DEV_ZONED_LOOP*? I hadn't seen that one before. I'm usually pretty up-to-date on block device definitions, but this caught me off guard. Wait, isn't *BLK_DEV_ZONED* the primary one I'd be looking at? Yeah, that rings a bell. Now, let's think… what about *NULL_BLK_ZONED*? Hmm, that could actually be relevant to my current train of thought. Before I dive too deep into *NULL_BLK_ZONED*, I'll quickly refresh my memory on the broader *NULL_BLK* context. Gotta make sure I'm thinking about the right abstractions here. |
| 10/2 | 2026/08/06 19:46 | tool | grepper |
0mArgs:{
"Expression": "config BLK_DEV_NULL_BLK"
}Results: {
"Output": "drivers/block/null_blk/Kconfig-5-\ndrivers/block/null_blk/Kconfig:6:config BLK_DEV_NULL_BLK\ndrivers/block/null_blk/Kconfig-7-\ttristate \"Null test block driver\"\n--\ndrivers/block/null_blk/Kconfig-9-\ndrivers/block/null_blk/Kconfig:10:config BLK_DEV_NULL_BLK_FAULT_INJECTION\ndrivers/block/null_blk/Kconfig-11-\tbool \"Support fault injection for Null test block driver\"\n"
} |
| 11/2 | 2026/08/06 19:46 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20535 output: 36 thoughts: 12 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Analyzing `NULL_BLK` Zoned Support** Right, let's dive into this. I'm thinking about `NULL_BLK` zoned support. Specifically, I need to start by understanding its implementation and how it's handled at the lowest level. I need to make sure I know how it will perform in a typical workload. I need to quickly assess how the zoned characteristics are leveraged, and where potential optimizations may be available within the current configurations and capabilities. Are we getting full utilization out of this feature? Are there any unexpected performance implications or compatibility issues I should be aware of, perhaps with specific controllers or drive models? My focus has to be on the details of block management. I need to get this right. |
| 12/2 | 2026/08/06 19:47 | tool | read-file |
0mArgs:{
"File": "drivers/block/null_blk/Kconfig",
"FirstLine": 1,
"LineCount": 50
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0\n 2:\t#\n 3:\t# Null block device driver configuration\n 4:\t#\n 5:\t\n 6:\tconfig BLK_DEV_NULL_BLK\n 7:\t\ttristate \"Null test block driver\"\n 8:\t\tselect CONFIGFS_FS\n 9:\t\n 10:\tconfig BLK_DEV_NULL_BLK_FAULT_INJECTION\n 11:\t\tbool \"Support fault injection for Null test block driver\"\n 12:\t\tdepends on BLK_DEV_NULL_BLK \u0026\u0026 FAULT_INJECTION_CONFIGFS\n"
} |
| 13/2 | 2026/08/06 19:47 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20915 output: 29 thoughts: 47 Thoughts: Okay, let's see... **Investigating the Zoned Configuration of `null_blk`** Hmm, it seems like `null_blk` might not have a dedicated, separate zoned configuration file. That's a bit unexpected. I need to clarify how it handles zoned block device functionality. My first instinct is to dive into the source code to confirm. I should start by looking at either `drivers/block/null_blk/main.c` or, if it exists, `drivers/block/null_blk/zoned.c` to see how it handles zoned block device specifics. |
| 14/2 | 2026/08/06 19:47 | tool | grepper |
0mArgs:{
"Expression": "zoned",
"PathPrefix": "drivers/block/null_blk/"
}Results: {
"Output": "drivers/block/null_blk/Makefile=10=endif\ndrivers/block/null_blk/Makefile:11:null_blk-$(CONFIG_BLK_DEV_ZONED) += zoned.o\n--\ndrivers/block/null_blk/main.c=234=MODULE_PARM_DESC(mbps, \"Limit maximum bandwidth (in MiB/s). Default: 0 (no limit)\");\ndrivers/block/null_blk/main.c-235-\ndrivers/block/null_blk/main.c:236:static bool g_zoned;\ndrivers/block/null_blk/main.c:237:module_param_named(zoned, g_zoned, bool, S_IRUGO);\ndrivers/block/null_blk/main.c:238:MODULE_PARM_DESC(zoned, \"Make device as a host-managed zoned block device. Default: false\");\ndrivers/block/null_blk/main.c-239-\n--\ndrivers/block/null_blk/main.c=241=module_param_named(zone_size, g_zone_size, ulong, S_IRUGO);\ndrivers/block/null_blk/main.c:242:MODULE_PARM_DESC(zone_size, \"Zone size in MB when block device is zoned. Must be power-of-two: Default: 256\");\ndrivers/block/null_blk/main.c-243-\n--\ndrivers/block/null_blk/main.c=245=module_param_named(zone_capacity, g_zone_capacity, ulong, 0444);\ndrivers/block/null_blk/main.c:246:MODULE_PARM_DESC(zone_capacity, \"Zone capacity in MB when block device is zoned. Can be less than or equal to zone size. Default: Zone size\");\ndrivers/block/null_blk/main.c-247-\n--\ndrivers/block/null_blk/main.c=249=module_param_named(zone_nr_conv, g_zone_nr_conv, uint, 0444);\ndrivers/block/null_blk/main.c:250:MODULE_PARM_DESC(zone_nr_conv, \"Number of conventional zones when block device is zoned. Default: 0\");\ndrivers/block/null_blk/main.c-251-\n--\ndrivers/block/null_blk/main.c=253=module_param_named(zone_max_open, g_zone_max_open, uint, 0444);\ndrivers/block/null_blk/main.c:254:MODULE_PARM_DESC(zone_max_open, \"Maximum number of open zones when block device is zoned. Default: 0 (no limit)\");\ndrivers/block/null_blk/main.c-255-\n--\ndrivers/block/null_blk/main.c=257=module_param_named(zone_max_active, g_zone_max_active, uint, 0444);\ndrivers/block/null_blk/main.c:258:MODULE_PARM_DESC(zone_max_active, \"Maximum number of active zones when block device is zoned. Default: 0 (no limit)\");\ndrivers/block/null_blk/main.c-259-\n--\ndrivers/block/null_blk/main.c=266=module_param_named(zone_full, g_zone_full, bool, S_IRUGO);\ndrivers/block/null_blk/main.c:267:MODULE_PARM_DESC(zone_full, \"Initialize the sequential write required zones of a zoned device to be full. Default: false\");\ndrivers/block/null_blk/main.c-268-\n--\ndrivers/block/null_blk/main.c=461=NULLB_DEVICE_ATTR(cache_size, ulong, NULL);\ndrivers/block/null_blk/main.c:462:NULLB_DEVICE_ATTR(zoned, bool, NULL);\ndrivers/block/null_blk/main.c-463-NULLB_DEVICE_ATTR(zone_size, ulong, NULL);\n--\ndrivers/block/null_blk/main.c=596=static struct configfs_attribute *nullb_device_attrs[] = {\n--\ndrivers/block/null_blk/main.c-632-\t\u0026nullb_device_attr_zone_size,\ndrivers/block/null_blk/main.c:633:\t\u0026nullb_device_attr_zoned,\ndrivers/block/null_blk/main.c-634-\tNULL,\n--\ndrivers/block/null_blk/main.c=777=static struct nullb_device *null_alloc_dev(void)\n--\ndrivers/block/null_blk/main.c-815-\tdev-\u003euse_per_node_hctx = g_use_per_node_hctx;\ndrivers/block/null_blk/main.c:816:\tdev-\u003ezoned = g_zoned;\ndrivers/block/null_blk/main.c-817-\tdev-\u003ezone_size = g_zone_size;\n--\ndrivers/block/null_blk/main.c=834=static void null_free_dev(struct nullb_device *dev)\n--\ndrivers/block/null_blk/main.c-838-\ndrivers/block/null_blk/main.c:839:\tnull_free_zoned_dev(dev);\ndrivers/block/null_blk/main.c-840-\tbadblocks_exit(\u0026dev-\u003ebadblocks);\n--\ndrivers/block/null_blk/main.c=1242=static blk_status_t null_transfer(struct nullb *nullb, struct page *page,\n--\ndrivers/block/null_blk/main.c-1252-\tif (!is_write) {\ndrivers/block/null_blk/main.c:1253:\t\tif (dev-\u003ezoned) {\ndrivers/block/null_blk/main.c-1254-\t\t\tvalid_len = null_zone_valid_read_len(nullb,\n--\ndrivers/block/null_blk/main.c=1442=static void null_handle_cmd(struct nullb_cmd *cmd, sector_t sector,\n--\ndrivers/block/null_blk/main.c-1453-\ndrivers/block/null_blk/main.c:1454:\tif (dev-\u003ezoned)\ndrivers/block/null_blk/main.c:1455:\t\tsts = null_process_zoned_cmd(cmd, op, sector, nr_sectors);\ndrivers/block/null_blk/main.c-1456-\telse\n--\ndrivers/block/null_blk/main.c=1619=static enum blk_eh_timer_return null_timeout_rq(struct request *rq)\n--\ndrivers/block/null_blk/main.c-1639-\t/*\ndrivers/block/null_blk/main.c:1640:\t * If the device is marked as blocking (i.e. memory backed or zoned\ndrivers/block/null_blk/main.c-1641-\t * device), the submission path may be blocked waiting for resources\n--\ndrivers/block/null_blk/main.c=1789=static void null_config_discard(struct nullb *nullb, struct queue_limits *lim)\n--\ndrivers/block/null_blk/main.c-1799-\ndrivers/block/null_blk/main.c:1800:\tif (nullb-\u003edev-\u003ezoned) {\ndrivers/block/null_blk/main.c-1801-\t\tnullb-\u003edev-\u003ediscard = false;\ndrivers/block/null_blk/main.c:1802:\t\tpr_info(\"discard option is ignored in zoned mode\\n\");\ndrivers/block/null_blk/main.c-1803-\t\treturn;\n--\ndrivers/block/null_blk/main.c=1885=static int null_validate_conf(struct nullb_device *dev)\n--\ndrivers/block/null_blk/main.c-1918-\ndrivers/block/null_blk/main.c:1919:\tif (dev-\u003ezoned \u0026\u0026\ndrivers/block/null_blk/main.c-1920-\t (!dev-\u003ezone_size || !is_power_of_2(dev-\u003ezone_size))) {\n--\ndrivers/block/null_blk/main.c=1955=static int null_add_dev(struct nullb_device *dev)\n--\ndrivers/block/null_blk/main.c-1991-\tnull_config_discard(nullb, \u0026lim);\ndrivers/block/null_blk/main.c:1992:\tif (dev-\u003ezoned) {\ndrivers/block/null_blk/main.c:1993:\t\trv = null_init_zoned_dev(dev, \u0026lim);\ndrivers/block/null_blk/main.c-1994-\t\tif (rv)\n--\ndrivers/block/null_blk/main.c-2045-\ndrivers/block/null_blk/main.c:2046:\tif (nullb-\u003edev-\u003ezoned) {\ndrivers/block/null_blk/main.c:2047:\t\trv = null_register_zoned_dev(nullb);\ndrivers/block/null_blk/main.c-2048-\t\tif (rv)\n--\ndrivers/block/null_blk/main.c-2066-out_cleanup_zone:\ndrivers/block/null_blk/main.c:2067:\tnull_free_zoned_dev(dev);\ndrivers/block/null_blk/main.c-2068-out_cleanup_tags:\n--\ndrivers/block/null_blk/null_blk.h=53=struct nullb_device {\n--\ndrivers/block/null_blk/null_blk.h-81-\tunsigned long cache_size; /* disk cache size in MB */\ndrivers/block/null_blk/null_blk.h:82:\tunsigned long zone_size; /* zone size in MB if device is zoned */\ndrivers/block/null_blk/null_blk.h:83:\tunsigned long zone_capacity; /* zone capacity in MB if device is zoned */\ndrivers/block/null_blk/null_blk.h-84-\tunsigned int zone_nr_conv; /* number of conventional zones */\n--\ndrivers/block/null_blk/null_blk.h-104-\tbool discard; /* if support discard */\ndrivers/block/null_blk/null_blk.h:105:\tbool zoned; /* if device is zoned */\ndrivers/block/null_blk/null_blk.h-106-\tbool zone_full; /* Initialize zones to be full */\n--\ndrivers/block/null_blk/null_blk.h=138=blk_status_t null_handle_memory_backed(struct nullb_cmd *cmd, enum req_op op,\n--\ndrivers/block/null_blk/null_blk.h-141-#ifdef CONFIG_BLK_DEV_ZONED\ndrivers/block/null_blk/null_blk.h:142:int null_init_zoned_dev(struct nullb_device *dev, struct queue_limits *lim);\ndrivers/block/null_blk/null_blk.h:143:int null_register_zoned_dev(struct nullb *nullb);\ndrivers/block/null_blk/null_blk.h:144:void null_free_zoned_dev(struct nullb_device *dev);\ndrivers/block/null_blk/null_blk.h-145-int null_report_zones(struct gendisk *disk, sector_t sector,\n--\ndrivers/block/null_blk/null_blk.h-147-\t\t struct blk_report_zones_args *args);\ndrivers/block/null_blk/null_blk.h:148:blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_op op,\ndrivers/block/null_blk/null_blk.h-149-\t\t\t\t sector_t sector, sector_t nr_sectors);\n--\ndrivers/block/null_blk/null_blk.h=152=ssize_t zone_cond_store(struct nullb_device *dev, const char *page,\n--\ndrivers/block/null_blk/null_blk.h-154-#else\ndrivers/block/null_blk/null_blk.h:155:static inline int null_init_zoned_dev(struct nullb_device *dev,\ndrivers/block/null_blk/null_blk.h-156-\t\tstruct queue_limits *lim)\n--\ndrivers/block/null_blk/null_blk.h-160-}\ndrivers/block/null_blk/null_blk.h:161:static inline int null_register_zoned_dev(struct nullb *nullb)\ndrivers/block/null_blk/null_blk.h-162-{\n--\ndrivers/block/null_blk/null_blk.h-164-}\ndrivers/block/null_blk/null_blk.h:165:static inline void null_free_zoned_dev(struct nullb_device *dev) {}\ndrivers/block/null_blk/null_blk.h:166:static inline blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd,\ndrivers/block/null_blk/null_blk.h-167-\t\t\tenum req_op op, sector_t sector, sector_t nr_sectors)\n--\ndrivers/block/null_blk/zoned.c=42=static inline void null_unlock_zone(struct nullb_device *dev,\n--\ndrivers/block/null_blk/zoned.c-50-\ndrivers/block/null_blk/zoned.c:51:int null_init_zoned_dev(struct nullb_device *dev,\ndrivers/block/null_blk/zoned.c-52-\t\t\tstruct queue_limits *lim)\n--\ndrivers/block/null_blk/zoned.c-79-\t * zone at the same time as such zone configuration does not correspond\ndrivers/block/null_blk/zoned.c:80:\t * to any real zoned device.\ndrivers/block/null_blk/zoned.c-81-\t */\n--\ndrivers/block/null_blk/zoned.c-174-\ndrivers/block/null_blk/zoned.c:175:int null_register_zoned_dev(struct nullb *nullb)\ndrivers/block/null_blk/zoned.c-176-{\n--\ndrivers/block/null_blk/zoned.c-186-\ndrivers/block/null_blk/zoned.c:187:void null_free_zoned_dev(struct nullb_device *dev)\ndrivers/block/null_blk/zoned.c-188-{\n--\ndrivers/block/null_blk/zoned.c=646=static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_op op,\n--\ndrivers/block/null_blk/zoned.c-707-\ndrivers/block/null_blk/zoned.c:708:blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_op op,\ndrivers/block/null_blk/zoned.c-709-\t\t\t\t sector_t sector, sector_t nr_sectors)\n--\ndrivers/block/null_blk/zoned.c=778=ssize_t zone_cond_store(struct nullb_device *dev, const char *page,\n--\ndrivers/block/null_blk/zoned.c-784-\ndrivers/block/null_blk/zoned.c:785:\tif (!dev-\u003ezoned) {\ndrivers/block/null_blk/zoned.c:786:\t\tpr_err(\"null_blk device is not zoned\\n\");\ndrivers/block/null_blk/zoned.c-787-\t\treturn -EINVAL;\n"
} |
| 15/2 | 2026/08/06 19:47 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 25082 output: 36 thoughts: 537 Thoughts: **Analyzing Zoned Block Device Support** Okay, so it looks like `null_blk` supports zoned devices – that's good. I remember it uses either the `g_zoned` parameter or configfs, so that's the angle. I see the importance of `BLK_DEV_NULL_BLK` and `BLK_DEV_ZONED` in the config. I have to check `BLK_DEV_ZONED_LOOP` again...wait, isn't that not mainline? Hmm, the `grep` results from the source say otherwise. Oh, I see – it's there in the Kconfig. Right, `BLK_DEV_ZONED` is the main one. Now I need to zero in on the key functions in the patch. I'm seeing changes in `block/blk-zoned.c`, so I'll review those: * `bdev_check_zone_mgmt` * `disk_zone_set_cond` * `disk_zone_get_state` * `disk_zone_get_cond` * `disk_zone_is_offline_or_readonly` * `disk_zone_is_seq` * `disk_insert_zone_wplug` * `disk_zone_wplug_abort` * `disk_zone_wplug_sync_state` * `blkdev_get_zone_info` * `blk_zone_reset_all_bio_endio` * `blk_zone_wplug_prepare_bio` * `blk_zone_wplug_handle_write` * `disk_alloc_zone_resources` * `disk_revalidate_zone_resources` * `blk_revalidate_disk_zones` I need to filter out the static ones that are inlined or less accessible, although syzkaller has a knack for finding them even if they're not directly exposed. Let's see... `bdev_check_zone_mgmt` is exported, that's interesting. What about `blkdev_get_zone_info`? No, it's not exported, though it is used in `block/ioctl.c`. And `blk_revalidate_disk_zones` is also exported. The rest are static: `disk_alloc_zone_resources`, `disk_revalidate_zone_resources`, `disk_zone_wplug_abort`, and `disk_zone_wplug_sync_state`. Okay, so I will now focus on those that are either non-static, or exported. |
| 16/2 | 2026/08/06 19:47 | tool | grepper |
0mArgs:{
"Expression": "EXPORT_SYMBOL_GPL\\(.*\\)",
"PathPrefix": "block/blk-zoned.c"
}Results: {
"Output": "block/blk-zoned.c=224=const char *blk_zone_cond_str(enum blk_zone_cond zone_cond)\n--\nblock/blk-zoned.c-232-}\nblock/blk-zoned.c:233:EXPORT_SYMBOL_GPL(blk_zone_cond_str);\nblock/blk-zoned.c-234-\n--\nblock/blk-zoned.c=302=bool bdev_zone_is_seq(struct block_device *bdev, sector_t sector)\n--\nblock/blk-zoned.c-308-}\nblock/blk-zoned.c:309:EXPORT_SYMBOL_GPL(bdev_zone_is_seq);\nblock/blk-zoned.c-310-\n--\nblock/blk-zoned.c=380=int blkdev_report_zones(struct block_device *bdev, sector_t sector,\n--\nblock/blk-zoned.c-389-}\nblock/blk-zoned.c:390:EXPORT_SYMBOL_GPL(blkdev_report_zones);\nblock/blk-zoned.c-391-\n--\nblock/blk-zoned.c=416=int blkdev_zone_mgmt(struct block_device *bdev, enum req_op op,\n--\nblock/blk-zoned.c-466-}\nblock/blk-zoned.c:467:EXPORT_SYMBOL_GPL(blkdev_zone_mgmt);\nblock/blk-zoned.c-468-\n--\nblock/blk-zoned.c=1010=int disk_report_zone(struct gendisk *disk, struct blk_zone *zone,\n--\nblock/blk-zoned.c-1037-}\nblock/blk-zoned.c:1038:EXPORT_SYMBOL_GPL(disk_report_zone);\nblock/blk-zoned.c-1039-\n--\nblock/blk-zoned.c=1093=int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,\n--\nblock/blk-zoned.c-1185-}\nblock/blk-zoned.c:1186:EXPORT_SYMBOL_GPL(blkdev_get_zone_info);\nblock/blk-zoned.c-1187-\n--\nblock/blk-zoned.c=1203=int blkdev_report_zones_cached(struct block_device *bdev, sector_t sector,\n--\nblock/blk-zoned.c-1243-}\nblock/blk-zoned.c:1244:EXPORT_SYMBOL_GPL(blkdev_report_zones_cached);\nblock/blk-zoned.c-1245-\n--\nblock/blk-zoned.c=1748=bool blk_zone_plug_bio(struct bio *bio, unsigned int nr_segs)\n--\nblock/blk-zoned.c-1785-}\nblock/blk-zoned.c:1786:EXPORT_SYMBOL_GPL(blk_zone_plug_bio);\nblock/blk-zoned.c-1787-\n--\nblock/blk-zoned.c=2496=int blk_revalidate_disk_zones(struct gendisk *disk)\n--\nblock/blk-zoned.c-2556-}\nblock/blk-zoned.c:2557:EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);\nblock/blk-zoned.c-2558-\n--\nblock/blk-zoned.c=2571=int blk_zone_issue_zeroout(struct block_device *bdev, sector_t sector,\n--\nblock/blk-zoned.c-2599-}\nblock/blk-zoned.c:2600:EXPORT_SYMBOL_GPL(blk_zone_issue_zeroout);\nblock/blk-zoned.c-2601-\n"
} |
| 17/2 | 2026/08/06 19:47 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 27200 output: 244 thoughts: 1359 Thoughts: **Patch Analysis and Fuzzing Strategy: Zoned Block Device Refactor** Okay, let's break this down. This patch is touching core aspects of the zoned block device implementation, so we need to be thorough. The initial glance at the diffs highlights several functions being modified, and I'm honing in on the key ones for fuzzing and testing. First, I noticed `bdev_check_zone_mgmt` is newly introduced. I had to double-check its visibility, but since it's used within the block layer's built-in components, it doesn't need to be exported. Then there are some other important functions `blkdev_get_zone_info`, `blk_revalidate_disk_zones`, `disk_report_zone` and so on, which are obviously core. My initial focus symbols were: `bdev_check_zone_mgmt`, `blkdev_get_zone_info`, `blk_revalidate_disk_zones`, and `disk_report_zone`, but I'm refining that list as I dive deeper. It seems like the core logic revolves around these functions for managing and retrieving zone information. I also had to keep in mind `submit_bio_noacct` as it's a hot path, and any changes here need very careful consideration, especially with the use of `bdev_check_zone_mgmt`. I'm slightly hesitant to directly target `submit_bio_noacct` directly in fuzzing, given its importance, but it's crucial to understand how it's impacted. Diving into the source, I confirm that `bdev_check_zone_mgmt` is indeed a `bool` function, taking `struct block_device` and `sector_t` as arguments. I also verified the signatures of `blk_revalidate_disk_zones`, which is `int` returning function on a `gendisk`, and `blkdev_get_zone_info`, which is `int` with a block device, sector, and `blk_zone` structure. I'm noting the signatures because these are crucial for constructing meaningful fuzz inputs later. Importantly, I will need to consider `disk_release_zone_resources` and `disk_init_zone_resources`. These are worth including. I also see that `disk_zone_wplug_abort`, `blk_zone_reset_all_bio_endio`, and `blk_zone_wplug_prepare_bio` have all been modified, so those are definitely important. For enabling the appropriate environment, I need to make sure to configure the kernel with `BLK_DEV_ZONED`, `BLK_DEV_NULL_BLK`, and `BLK_DEV_ZONED_LOOP`. Since `BLK_DEV_NULL_BLK` is tristate, I'll need to remember that if I want it built-in, I need to explicitly define it. Summarizing, the patch focuses on refactoring zone condition tracking within the block layer using `enum blk_zstate` and a `zones_state` array. It’s updating zone management, revalidation, and information retrieval functions, along with partition disabling on zoned devices. My primary focus for fuzzing will be: * `bdev_check_zone_mgmt` * `blkdev_get_zone_info` * `blk_revalidate_disk_zones` * `disk_release_zone_resources` * `disk_init_zone_resources` I'll use `BLK_DEV_ZONED`, `BLK_DEV_NULL_BLK`, and `BLK_DEV_ZONED_LOOP` configs, ensuring `BLK_DEV_NULL_BLK` is explicitly defined if I want it built-in. This should ensure comprehensive coverage of the refactored functionality and help detect any regressions in zone state handling or bio submission. I feel confident with this assessment. |
| 18/2 | 2026/08/06 19:47 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"BLK_DEV_ZONED",
"BLK_DEV_NULL_BLK",
"BLK_DEV_ZONED_LOOP"
],
"FocusSymbols": [
"bdev_check_zone_mgmt",
"blkdev_get_zone_info",
"blk_revalidate_disk_zones",
"disk_release_zone_resources",
"disk_init_zone_resources"
],
"Reasoning": "The patch refactors how zone conditions are tracked in the block layer, introducing `enum blk_zstate` and a new `zones_state` array to replace `zones_cond`. It updates zone management checks (`bdev_check_zone_mgmt`), zone revalidation (`blk_revalidate_disk_zones`), and zone information retrieval (`blkdev_get_zone_info`). It also modifies `__add_disk` to disable partitions on zoned block devices. These are functional changes to the core block layer's zoned storage support and should be fuzzed to ensure no regressions in zone state tracking or bio submission.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"BLK_DEV_ZONED",
"BLK_DEV_NULL_BLK",
"BLK_DEV_ZONED_LOOP"
],
"FocusSymbols": [
"bdev_check_zone_mgmt",
"blkdev_get_zone_info",
"blk_revalidate_disk_zones",
"disk_release_zone_resources",
"disk_init_zone_resources"
],
"Reasoning": "The patch refactors how zone conditions are tracked in the block layer, introducing `enum blk_zstate` and a new `zones_state` array to replace `zones_cond`. It updates zone management checks (`bdev_check_zone_mgmt`), zone revalidation (`blk_revalidate_disk_zones`), and zone information retrieval (`blkdev_get_zone_info`). It also modifies `__add_disk` to disable partitions on zoned block devices. These are functional changes to the core block layer's zoned storage support and should be fuzzed to ensure no regressions in zone state tracking or bio submission.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|