| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/11 12:00 | flow | patch-triage |
3mArgs:null Results: {
"EnableConfigs": [
"BLK_DEV_ZONED",
"BLK_DEV_NULL_BLK",
"BLK_DEV_ZONED_LOOP",
"BLK_DEV_UBLK"
],
"FocusSymbols": [
"blk_revalidate_disk_zones",
"blkdev_report_zones_ioctl",
"blkdev_zone_mgmt_ioctl",
"bdev_check_zone_mgmt",
"blkdev_get_zone_info"
],
"KMSANReasoning": "The patch refactors how zone conditions are stored internally in the block layer, replacing the `zones_cond` array with a `zones_state` array that packs both zone condition and zone type into a single byte. It also adds checks to prevent issuing zone management commands or writes to offline or read-only zones.\n\nAll newly introduced or modified memory allocations for these internal states (e.g., `args-\u003ezones_state` in `disk_init_revalidate_args`) use `kzalloc`, ensuring they are fully zero-initialized. The `struct blk_zone` structure, which is eventually copied to user space via `blkdev_copy_zone_to_user`, is allocated on the stack in `blkdev_report_zones_cached` but is fully zeroed out using `memset(zone, 0, sizeof(*zone))` in `blkdev_get_zone_info` before any fields are populated. \n\nSince all relevant structures and arrays are explicitly zero-initialized, there is no risk of uninitialized memory reads or kernel-to-user info-leaks. Any potential bugs introduced by this patch (such as out-of-bounds accesses, use-after-free, or logic errors) would be effectively caught by standard KASAN, LOCKDEP, and other default bug detectors. Therefore, a dedicated KMSAN fuzzing session is not required.",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies the core block layer's handling of zoned block devices. It introduces a new `enum blk_zstate` to combine zone condition and zone type, replacing the previous `zones_cond` array with `zones_state`. It adds `bdev_check_zone_mgmt` to validate sectors for zone management operations, ensuring they are not offline or read-only. It also modifies zone write plug handling, including aborting plugged BIOs for offline/read-only zones. These changes affect core block layer logic for zoned block devices, which can be reached via software-emulated zoned devices like `null_blk`, `zloop`, or `ublk`. The changes are functional and reachable, so they are worth fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/11 12:00 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 490015abd3b7223e282686e5da557289054de9c9\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Tue Aug 11 12:00:36 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..02f92d113d324 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 = BLK_ZFLAG_CONV;\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@@ -827,6 +980,12 @@ static unsigned int disk_zone_wplug_sync_wp_offset(struct gendisk *disk,\n \t\tunsigned long flags;\n \n \t\tspin_lock_irqsave(\u0026zwplug-\u003elock, flags);\n+\t\tif (disk_zone_cond_is_offline_or_readonly(zone-\u003econd)) {\n+\t\t\tzwplug-\u003eflags \u0026= ~BLK_ZONE_WPLUG_NEED_WP_UPDATE;\n+\t\t\tzwplug-\u003econd = zone-\u003econd;\n+\t\t\tzwplug-\u003ewp_offset = UINT_MAX;\n+\t\t\tdisk_mark_zone_wplug_dead(zwplug);\n+\t\t}\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\tspin_unlock_irqrestore(\u0026zwplug-\u003elock, flags);\n@@ -871,7 +1030,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 +1099,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 +1114,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 +1135,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 +1146,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 +1270,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 +1541,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 +1603,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 +2015,23 @@ 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+\tif (!lim-\u003echunk_sectors)\n+\t\treturn 0;\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 +2041,24 @@ 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, pool_size, i;\n \tint ret = -ENOMEM;\n \n-\tatomic_set(\u0026disk-\u003enr_zone_wplugs, 0);\n+\tnr_zones = disk_get_nr_zones(disk);\n+\tif (!nr_zones)\n+\t\treturn -ENODEV;\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 = 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 +2076,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 +2086,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 +2141,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 +2161,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 +2184,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 +2211,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_set_zones_state_array(disk, args-\u003ezones_state);\n+\targs-\u003ezones_state = NULL;\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 \n \t/*\n \t * Some devices can advertise zone resource limits that are larger than\n@@ -2120,48 +2272,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) || !nr_zones)\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 +2332,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 +2367,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 +2409,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 +2467,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 +2503,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 +2547,19 @@ 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+\tdisk-\u003enr_zones = 0;\n \n \treturn ret;\n }\n@@ -2435,8 +2610,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 +2637,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/11 12:00 | agent | patch-evaluator |
2mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"BLK_DEV_ZONED",
"BLK_DEV_NULL_BLK",
"BLK_DEV_ZONED_LOOP",
"BLK_DEV_UBLK"
],
"FocusSymbols": [
"blk_revalidate_disk_zones",
"blkdev_report_zones_ioctl",
"blkdev_zone_mgmt_ioctl",
"bdev_check_zone_mgmt",
"blkdev_get_zone_info"
],
"Reasoning": "The patch modifies the core block layer's handling of zoned block devices. It introduces a new `enum blk_zstate` to combine zone condition and zone type, replacing the previous `zones_cond` array with `zones_state`. It adds `bdev_check_zone_mgmt` to validate sectors for zone management operations, ensuring they are not offline or read-only. It also modifies zone write plug handling, including aborting plugged BIOs for offline/read-only zones. These changes affect core block layer logic for zoned block devices, which can be reached via software-emulated zoned devices like `null_blk`, `zloop`, or `ublk`. The changes are functional and reachable, so they are worth fuzzing.",
"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 490015abd3b7223e282686e5da557289054de9c9
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Aug 11 12:00:36 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..02f92d113d324 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 = BLK_ZFLAG_CONV;
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);
@@ -827,6 +980,12 @@ static unsigned int disk_zone_wplug_sync_wp_offset(struct gendisk *disk,
unsigned long flags;
spin_lock_irqsave(&zwplug->lock, flags);
+ if (disk_zone_cond_is_offline_or_readonly(zone->cond)) {
+ zwplug->flags &= ~BLK_ZONE_WPLUG_NEED_WP_UPDATE;
+ zwplug->cond = zone->cond;
+ zwplug->wp_offset = UINT_MAX;
+ disk_mark_zone_wplug_dead(zwplug);
+ }
if (zwplug->flags & BLK_ZONE_WPLUG_NEED_WP_UPDATE)
disk_zone_wplug_set_wp_offset(disk, zwplug, wp_offset);
spin_unlock_irqrestore(&zwplug->lock, flags);
@@ -871,7 +1030,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 +1099,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 +1114,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 +1135,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 +1146,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 +1270,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 +1541,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 +1603,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 +2015,23 @@ 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;
+
+ if (!lim->chunk_sectors)
+ return 0;
+
+ 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 +2041,24 @@ 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, pool_size, i;
int ret = -ENOMEM;
- atomic_set(&disk->nr_zone_wplugs, 0);
+ nr_zones = disk_get_nr_zones(disk);
+ if (!nr_zones)
+ return -ENODEV;
+
+ /*
+ * 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 +2076,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 +2086,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 +2141,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 +2161,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 +2184,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 +2211,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_set_zones_state_array(disk, args->zones_state);
+ args->zones_state = NULL;
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;
/*
* Some devices can advertise zone resource limits that are larger than
@@ -2120,48 +2272,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) || !nr_zones)
+ 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 +2332,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 +2367,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 +2409,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 +2467,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 +2503,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 +2547,19 @@ 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);
+ disk->nr_zones = 0;
return ret;
}
@@ -2435,8 +2610,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 +2637,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/11 12:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17782 output: 22 thoughts: 537 Thoughts: **Analyzing a Zoned Block Device Patch** Okay, so I've got a patch here that's digging deep into the block layer, specifically around zoned block devices (ZBDs). It's touching a bunch of core files: `blk-core.c`, `blk-zoned.c`, `blk.h`, `genhd.c`, and `blkdev.h`. The gist of it is a refactoring of how zone conditions are handled. This looks significant. The patch is introducing this `enum blk_zstate` which neatly packages the zone condition and type into a single byte. That's a good simplification, potentially leading to performance benefits. It's also renaming `zones_cond` to `zones_state` in the `gendisk` structure – a straightforward change, but worth noting. The addition of `bdev_check_zone_mgmt` to check if a sector is valid and writeable is interesting. That's a new gatekeeper, and it's being integrated into `submit_bio_noacct`, which is a very hot path. I'll need to think about fuzzing that carefully, as it's a critical spot. Zone write plug handling is also being tweaked, and that's an area that's prone to race conditions if not done correctly. The change to `__add_disk`, setting `GENHD_FL_NO_PART` if the device is zoned, that makes sense - you don't want to partition scan a ZBD. This is definitely a functional change to the ZBD implementation, so fuzzing is a *must*. Now, the important question is: where to focus? `submit_bio_noacct` is a likely target, but it's a high-traffic area, so I should be careful. I see modifications to `blk_revalidate_disk_zones`, `disk_revalidate_zone_resources`, `blkdev_get_zone_info`, and the ioctls: `blkdev_report_zones_ioctl` and `blkdev_zone_mgmt_ioctl`. These are my entry points. There's also `bdev_check_zone_mgmt`, and changes to the zone write plug handling functions: `disk_zone_wplug_abort`, `disk_alloc_zone_resources`, `disk_init_zone_resources`, `disk_release_zone_resources`, and `blk_zone_wplug_handle_write`. That gives me a good starting point. Regarding configurations, `CONFIG_BLK_DEV_ZONED` is, obviously, critical. I should definitely add that to my `EnableConfigs`. For testing, I should look into using `BLK_DEV_NULL_BLK` alongside `BLK_DEV_ZONED` to test in virtualized environments. I'll start by searching the codebase for usages of `bdev_check_zone_mgmt` and `blk_revalidate_disk_zones`, to get a complete picture of its usage, and potential call sites that I can inject malformed data to. |
| 4/2 | 2026/08/11 12:00 | tool | grepper |
0mArgs:{
"Expression": "bdev_check_zone_mgmt"
}Results: {
"Output": "block/blk-core.c=817=void submit_bio_noacct(struct bio *bio)\n--\nblock/blk-core.c-907-\t\t */\nblock/blk-core.c:908:\t\tif (!bdev_check_zone_mgmt(bdev, bio-\u003ebi_iter.bi_sector))\nblock/blk-core.c-909-\t\t\tgoto end_io;\n--\nblock/blk-zoned.c=309=EXPORT_SYMBOL_GPL(bdev_zone_is_seq);\n--\nblock/blk-zoned.c-311-/**\nblock/blk-zoned.c:312: * bdev_check_zone_mgmt - check if a sector belongs to a valid sequential zone\nblock/blk-zoned.c-313- * @bdev: block device to check\n--\nblock/blk-zoned.c-318- */\nblock/blk-zoned.c:319:bool bdev_check_zone_mgmt(struct block_device *bdev, sector_t sector)\nblock/blk-zoned.c-320-{\n--\nblock/blk.h=578=int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,\nblock/blk.h-579-\t\tunsigned int cmd, unsigned long arg);\nblock/blk.h:580:bool bdev_check_zone_mgmt(struct block_device *bdev, sector_t sector);\nblock/blk.h-581-#else /* CONFIG_BLK_DEV_ZONED */\n--\nblock/blk.h=618=static inline int blkdev_zone_mgmt_ioctl(struct block_device *bdev,\n--\nblock/blk.h-622-}\nblock/blk.h:623:static inline bool bdev_check_zone_mgmt(struct block_device *bdev,\nblock/blk.h-624-\t\t\t\t\tsector_t sector)\n"
} |
| 5/2 | 2026/08/11 12:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19488 output: 22 thoughts: 14 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in this field, presented as a first-person summary: **Diving into `blk_revalidate_disk_zones`** Alright, time to dig in. The focus is clearly on the function `blk_revalidate_disk_zones`. This name immediately tells me we're dealing with zone-based storage, probably an SMR or Zoned Namespace drive, and we're looking at a routine responsible for either refreshing or validating the state of those zones. My initial thought is about what triggered this investigation. Was there a reported issue? Are we optimizing something? Or just doing some preventative maintenance? Knowing the context would help frame the exploration. I'll need to consider how this function interacts with the block layer, especially the zone management interfaces, and if it's operating on a per-device basis, or a logical volume, or higher-level storage construct. I'll need to keep in mind the potential performance implications of revalidation, especially if it's an online operation. My first steps are to look at the surrounding code, see what calls this function, and figure out when it's invoked. |
| 6/2 | 2026/08/11 12:00 | tool | grepper |
0mArgs:{
"Expression": "blk_revalidate_disk_zones"
}Results: {
"Output": "block/blk-zoned.c=627=static bool disk_insert_zone_wplug(struct gendisk *disk,\n--\nblock/blk-zoned.c-652-\t * attached to the disk, then this is a zone write plug insert from the\nblock/blk-zoned.c:653:\t * first call to blk_revalidate_disk_zones(), in which case the zone is\nblock/blk-zoned.c-654-\t * necessarilly in the active condition.\n--\nblock/blk-zoned.c=2427=static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,\n--\nblock/blk-zoned.c-2491-/**\nblock/blk-zoned.c:2492: * blk_revalidate_disk_zones - (re)allocate and initialize zone write plugs\nblock/blk-zoned.c-2493- * @disk:\tTarget disk\n--\nblock/blk-zoned.c-2503- */\nblock/blk-zoned.c:2504:int blk_revalidate_disk_zones(struct gendisk *disk)\nblock/blk-zoned.c-2505-{\n--\nblock/blk-zoned.c-2565-}\nblock/blk-zoned.c:2566:EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);\nblock/blk-zoned.c-2567-\n--\ndrivers/block/null_blk/zoned.c=175=int null_register_zoned_dev(struct nullb *nullb)\n--\ndrivers/block/null_blk/zoned.c-183-\ndrivers/block/null_blk/zoned.c:184:\treturn blk_revalidate_disk_zones(disk);\ndrivers/block/null_blk/zoned.c-185-}\n--\ndrivers/block/ublk_drv.c=559=static int ublk_get_nr_zones(const struct ublk_device *ub)\n--\ndrivers/block/ublk_drv.c-566-\ndrivers/block/ublk_drv.c:567:static int ublk_revalidate_disk_zones(struct ublk_device *ub)\ndrivers/block/ublk_drv.c-568-{\ndrivers/block/ublk_drv.c:569:\treturn blk_revalidate_disk_zones(ub-\u003eub_disk);\ndrivers/block/ublk_drv.c-570-}\n--\ndrivers/block/ublk_drv.c=770=static void ublk_dev_param_zoned_apply(struct ublk_device *ub)\n--\ndrivers/block/ublk_drv.c-773-\ndrivers/block/ublk_drv.c:774:static int ublk_revalidate_disk_zones(struct ublk_device *ub)\ndrivers/block/ublk_drv.c-775-{\n--\ndrivers/block/ublk_drv.c=4447=static int ublk_ctrl_start_dev(struct ublk_device *ub,\n--\ndrivers/block/ublk_drv.c-4571-\tif (ublk_dev_is_zoned(ub)) {\ndrivers/block/ublk_drv.c:4572:\t\tret = ublk_revalidate_disk_zones(ub);\ndrivers/block/ublk_drv.c-4573-\t\tif (ret)\n--\ndrivers/block/virtio_blk.c=1438=static int virtblk_probe(struct virtio_device *vdev)\n--\ndrivers/block/virtio_blk.c-1537-\t (lim.features \u0026 BLK_FEAT_ZONED)) {\ndrivers/block/virtio_blk.c:1538:\t\terr = blk_revalidate_disk_zones(vblk-\u003edisk);\ndrivers/block/virtio_blk.c-1539-\t\tif (err)\n--\ndrivers/block/zloop.c=1185=static int zloop_ctl_add(struct zloop_options *opts)\n--\ndrivers/block/zloop.c-1326-\ndrivers/block/zloop.c:1327:\tret = blk_revalidate_disk_zones(zlo-\u003edisk);\ndrivers/block/zloop.c-1328-\tif (ret)\n--\ndrivers/md/dm-zone.c=150=bool dm_is_zone_write(struct mapped_device *md, struct bio *bio)\n--\ndrivers/md/dm-zone.c-168- * for zone append emulation. Note that we cannot simply use the block layer\ndrivers/md/dm-zone.c:169: * blk_revalidate_disk_zones() function here as the mapped device is suspended\ndrivers/md/dm-zone.c-170- * (this is called from __bind() context).\n--\ndrivers/md/dm-zone.c=172=int dm_revalidate_zones(struct dm_table *t, struct request_queue *q)\n--\ndrivers/md/dm-zone.c-198-\tmd-\u003erevalidate_map_task = current;\ndrivers/md/dm-zone.c:199:\tret = blk_revalidate_disk_zones(disk);\ndrivers/md/dm-zone.c-200-\tmd-\u003erevalidate_map_task = NULL;\n--\ndrivers/nvme/host/core.c=2387=static int nvme_update_ns_info_block(struct nvme_ns *ns,\n--\ndrivers/nvme/host/core.c-2499-\tif (blk_queue_is_zoned(ns-\u003equeue)) {\ndrivers/nvme/host/core.c:2500:\t\tret = blk_revalidate_disk_zones(ns-\u003edisk);\ndrivers/nvme/host/core.c-2501-\t\tif (ret \u0026\u0026 !nvme_first_scan(ns-\u003edisk))\n--\ndrivers/scsi/sd_zbc.c=519=static void sd_zbc_print_zones(struct scsi_disk *sdkp)\n--\ndrivers/scsi/sd_zbc.c-536-/*\ndrivers/scsi/sd_zbc.c:537: * Call blk_revalidate_disk_zones() if any of the zoned disk properties have\ndrivers/scsi/sd_zbc.c-538- * changed that make it necessary to call that function. Called by\n--\ndrivers/scsi/sd_zbc.c=541=int sd_zbc_revalidate_zones(struct scsi_disk *sdkp)\n--\ndrivers/scsi/sd_zbc.c-565-\tflags = memalloc_noio_save();\ndrivers/scsi/sd_zbc.c:566:\tret = blk_revalidate_disk_zones(disk);\ndrivers/scsi/sd_zbc.c-567-\tmemalloc_noio_restore(flags);\n--\ninclude/linux/blkdev.h=459=int blkdev_zone_mgmt(struct block_device *bdev, enum req_op op,\ninclude/linux/blkdev.h-460-\t\tsector_t sectors, sector_t nr_sectors);\ninclude/linux/blkdev.h:461:int blk_revalidate_disk_zones(struct gendisk *disk);\ninclude/linux/blkdev.h-462-\n"
} |
| 7/2 | 2026/08/11 12:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 21632 output: 21 thoughts: 238 Thoughts: **Reaching `blk_revalidate_disk_zones`** Okay, so I see that `blk_revalidate_disk_zones` is a pretty important function, and it's being called from several different places, including `null_register_zoned_dev` in `null_blk` and `ublk_revalidate_disk_zones` in `ublk_drv`. Interesting, it's also reached from `virtblk_probe`, `zloop_ctl_add`, `dm_revalidate_zones`, `nvme_update_ns_info_block`, and `sd_zbc_revalidate_zones`. The key takeaway here is that `null_blk` and `ublk` are both software-emulated block devices, which makes them prime targets for fuzzing. Given their nature, I can control a lot of the inputs, making it easy to generate a variety of interesting test cases. And because these functions are in the core block layer, any vulnerabilities found here could have significant downstream impact. Definitely worth the effort to dig into these. Now, let's take a look at `bdev_check_zone_mgmt`. |
| 8/2 | 2026/08/11 12:01 | tool | grepper |
0mArgs:{
"Expression": "submit_bio_noacct"
}Results: {
"Output": "Documentation/fault-injection/fault-injection.rst=8=Available fault injection capabilities\n--\nDocumentation/fault-injection/fault-injection.rst-34- /sys/block/\u003cdevice\u003e/make-it-fail or\nDocumentation/fault-injection/fault-injection.rst:35: /sys/block/\u003cdevice\u003e/\u003cpartition\u003e/make-it-fail. (submit_bio_noacct())\nDocumentation/fault-injection/fault-injection.rst-36-\n--\nDocumentation/trace/ftrace.rst=1578=function-trace, we get a much larger output::\n--\nDocumentation/trace/ftrace.rst-1628- =\u003e blk_queue_bio\nDocumentation/trace/ftrace.rst:1629: =\u003e submit_bio_noacct\nDocumentation/trace/ftrace.rst-1630- =\u003e submit_bio\n--\nDocumentation/trace/ftrace.rst=1871=tracers.\n--\nDocumentation/trace/ftrace.rst-1913- =\u003e blk_queue_bio\nDocumentation/trace/ftrace.rst:1914: =\u003e submit_bio_noacct\nDocumentation/trace/ftrace.rst-1915- =\u003e submit_bio\n--\nDocumentation/trace/histogram.rst=1994=histogram::\n--\nDocumentation/trace/histogram.rst-2022- __submit_bio+0xff/0x190\nDocumentation/trace/histogram.rst:2023: submit_bio_noacct_nocheck+0x25b/0x2b0\nDocumentation/trace/histogram.rst:2024: submit_bio_noacct+0x20b/0x600\nDocumentation/trace/histogram.rst-2025- submit_bio+0x28/0x90\n--\nblock/bio.c=388=static void bio_alloc_rescue(struct work_struct *work)\n--\nblock/bio.c-400-\nblock/bio.c:401:\t\tsubmit_bio_noacct(bio);\nblock/bio.c-402-\t}\n--\nblock/bio.c-405-/*\nblock/bio.c:406: * submit_bio_noacct() converts recursion to iteration; this means if we're\nblock/bio.c-407- * running beneath it, any bios we allocate and submit will not be submitted\n--\nblock/bio.c-410- * This exposes us to a potential deadlock if we allocate multiple bios from the\nblock/bio.c:411: * same bio_set while running underneath submit_bio_noacct(). If we were to\nblock/bio.c-412- * allocate multiple bios (say a stacking block driver that was splitting bios),\n--\nblock/bio.c=476=static struct bio *bio_alloc_percpu_cache(struct bio_set *bs)\n--\nblock/bio.c-517- *\nblock/bio.c:518: * Note that when running under submit_bio_noacct() (i.e. any block driver),\nblock/bio.c-519- * bios are not submitted until after you return - see the code in\nblock/bio.c:520: * submit_bio_noacct() that converts recursion into iteration, to prevent\nblock/bio.c-521- * stack overflows.\nblock/bio.c-522- *\nblock/bio.c:523: * This would normally mean allocating multiple bios under submit_bio_noacct()\nblock/bio.c-524- * would be susceptible to deadlocks, but we have\n--\nblock/bio.c-529- * mempools. Doing multiple allocations from the same mempool under\nblock/bio.c:530: * submit_bio_noacct() should be avoided - instead, use bio_set's front_pad\nblock/bio.c-531- * for per bio allocations.\n--\nblock/blk-core.c=670=static void __submit_bio(struct bio *bio)\n--\nblock/blk-core.c-694- * bio_list of new bios to be added. -\u003esubmit_bio() may indeed add some more\nblock/blk-core.c:695: * bios through a recursive call to submit_bio_noacct. If it did, we find a\nblock/blk-core.c-696- * non-NULL value in bio_list and re-enter the loop from the top.\n--\nblock/blk-core.c-704- */\nblock/blk-core.c:705:static void __submit_bio_noacct(struct bio *bio)\nblock/blk-core.c-706-{\n--\nblock/blk-core.c-748-\nblock/blk-core.c:749:static void __submit_bio_noacct_mq(struct bio *bio)\nblock/blk-core.c-750-{\n--\nblock/blk-core.c-761-\nblock/blk-core.c:762:void submit_bio_noacct_nocheck(struct bio *bio, bool split)\nblock/blk-core.c-763-{\n--\nblock/blk-core.c-789-\t} else if (!bdev_test_flag(bio-\u003ebi_bdev, BD_HAS_SUBMIT_BIO)) {\nblock/blk-core.c:790:\t\t__submit_bio_noacct_mq(bio);\nblock/blk-core.c-791-\t} else {\nblock/blk-core.c:792:\t\t__submit_bio_noacct(bio);\nblock/blk-core.c-793-\t}\n--\nblock/blk-core.c=796=static blk_status_t blk_validate_atomic_write_op_size(struct request_queue *q,\n--\nblock/blk-core.c-808-/**\nblock/blk-core.c:809: * submit_bio_noacct - re-submit a bio to the block device layer for I/O\nblock/blk-core.c-810- * @bio: The bio describing the location in memory and on the device.\n--\nblock/blk-core.c-816- */\nblock/blk-core.c:817:void submit_bio_noacct(struct bio *bio)\nblock/blk-core.c-818-{\n--\nblock/blk-core.c-927-\t\treturn;\nblock/blk-core.c:928:\tsubmit_bio_noacct_nocheck(bio, false);\nblock/blk-core.c-929-\treturn;\n--\nblock/blk-core.c-935-}\nblock/blk-core.c:936:EXPORT_SYMBOL(submit_bio_noacct);\nblock/blk-core.c-937-\n--\nblock/blk-core.c=959=void submit_bio(struct bio *bio)\n--\nblock/blk-core.c-968-\tbio_set_ioprio(bio);\nblock/blk-core.c:969:\tsubmit_bio_noacct(bio);\nblock/blk-core.c-970-}\n--\nblock/blk-merge.c=119=struct bio *bio_submit_split_bioset(struct bio *bio, unsigned int split_sectors,\n--\nblock/blk-merge.c-135-\telse if (!blk_throtl_bio(bio))\nblock/blk-merge.c:136:\t\tsubmit_bio_noacct_nocheck(bio, true);\nblock/blk-merge.c-137-\n--\nblock/blk-throttle.c=1207=static void blk_throtl_dispatch_work_fn(struct work_struct *work)\n--\nblock/blk-throttle.c-1228-\t\twhile ((bio = bio_list_pop(\u0026bio_list_on_stack)))\nblock/blk-throttle.c:1229:\t\t\tsubmit_bio_noacct_nocheck(bio, false);\nblock/blk-throttle.c-1230-\t\tblk_finish_plug(\u0026plug);\n--\nblock/blk.h=62=int __bio_queue_enter(struct request_queue *q, struct bio *bio);\nblock/blk.h:63:void submit_bio_noacct_nocheck(struct bio *bio, bool split);\nblock/blk.h-64-int bio_submit_or_kill(struct bio *bio, unsigned int flags);\n--\ndrivers/block/drbd/drbd_int.h=1485=void drbd_set_my_capacity(struct drbd_device *device, sector_t size);\n--\ndrivers/block/drbd/drbd_int.h-1489- */\ndrivers/block/drbd/drbd_int.h:1490:static inline void drbd_submit_bio_noacct(struct drbd_device *device,\ndrivers/block/drbd/drbd_int.h-1491-\t\t\t\t\t int fault_type, struct bio *bio)\n--\ndrivers/block/drbd/drbd_int.h-1494-\tif (!bio-\u003ebi_bdev) {\ndrivers/block/drbd/drbd_int.h:1495:\t\tdrbd_err(device, \"drbd_submit_bio_noacct: bio-\u003ebi_bdev == NULL\\n\");\ndrivers/block/drbd/drbd_int.h-1496-\t\tbio-\u003ebi_status = BLK_STS_IOERR;\n--\ndrivers/block/drbd/drbd_int.h-1503-\telse\ndrivers/block/drbd/drbd_int.h:1504:\t\tsubmit_bio_noacct(bio);\ndrivers/block/drbd/drbd_int.h-1505-}\n--\ndrivers/block/drbd/drbd_main.c=2215=static void do_retry(struct work_struct *ws)\n--\ndrivers/block/drbd/drbd_main.c-2258-\ndrivers/block/drbd/drbd_main.c:2259:\t\t/* We are not just doing submit_bio_noacct(),\ndrivers/block/drbd/drbd_main.c-2260-\t\t * as we want to keep the start_time information. */\n--\ndrivers/block/drbd/drbd_receiver.c=1446=int drbd_submit_peer_request(struct drbd_peer_request *peer_req)\n--\ndrivers/block/drbd/drbd_receiver.c-1533-\ndrivers/block/drbd/drbd_receiver.c:1534:\t\tdrbd_submit_bio_noacct(device, peer_request_fault_type(peer_req), bio);\ndrivers/block/drbd/drbd_receiver.c-1535-\t} while (bios);\n--\ndrivers/block/drbd/drbd_req.c=1152=drbd_submit_req_private_bio(struct drbd_request *req)\n--\ndrivers/block/drbd/drbd_req.c-1178-\t\telse\ndrivers/block/drbd/drbd_req.c:1179:\t\t\tsubmit_bio_noacct(bio);\ndrivers/block/drbd/drbd_req.c-1180-\t\tput_ldev(device);\n--\ndrivers/block/drbd/drbd_worker.c=1499=int w_restart_disk_io(struct drbd_work *w, int cancel)\n--\ndrivers/block/drbd/drbd_worker.c-1511-\treq-\u003eprivate_bio-\u003ebi_end_io = drbd_request_endio;\ndrivers/block/drbd/drbd_worker.c:1512:\tsubmit_bio_noacct(req-\u003eprivate_bio);\ndrivers/block/drbd/drbd_worker.c-1513-\n--\ndrivers/md/bcache/bcache.h=944=static inline void closure_bio_submit(struct cache_set *c,\n--\ndrivers/md/bcache/bcache.h-953-\t}\ndrivers/md/bcache/bcache.h:954:\tsubmit_bio_noacct(bio);\ndrivers/md/bcache/bcache.h-955-}\n--\ndrivers/md/bcache/btree.c=922=static struct btree *mca_alloc(struct cache_set *c, struct btree_op *op,\n--\ndrivers/md/bcache/btree.c-996- *\ndrivers/md/bcache/btree.c:997: * If IO is necessary and running under submit_bio_noacct, returns -EAGAIN.\ndrivers/md/bcache/btree.c-998- *\n--\ndrivers/md/bcache/request.c=1101=static void detached_dev_do_request(struct bcache_device *d,\n--\ndrivers/md/bcache/request.c-1126-\ndrivers/md/bcache/request.c:1127:\tsubmit_bio_noacct(clone_bio);\ndrivers/md/bcache/request.c-1128-}\n--\ndrivers/md/bcache/request.c=1170=void cached_dev_submit_bio(struct bio *bio)\n--\ndrivers/md/bcache/request.c-1212-\t\t\t * can't call bch_journal_meta from under\ndrivers/md/bcache/request.c:1213:\t\t\t * submit_bio_noacct\ndrivers/md/bcache/request.c-1214-\t\t\t */\n--\ndrivers/md/bcache/request.c=1279=void flash_dev_submit_bio(struct bio *bio)\n--\ndrivers/md/bcache/request.c-1298-\t\t/*\ndrivers/md/bcache/request.c:1299:\t\t * can't call bch_journal_meta from under submit_bio_noacct\ndrivers/md/bcache/request.c-1300-\t\t */\n--\ndrivers/md/dm-clone-target.c=318=static void submit_bios(struct bio_list *bios)\n--\ndrivers/md/dm-clone-target.c-325-\twhile ((bio = bio_list_pop(bios)))\ndrivers/md/dm-clone-target.c:326:\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm-clone-target.c-327-\n--\ndrivers/md/dm-clone-target.c=339=static void issue_bio(struct clone *clone, struct bio *bio)\n--\ndrivers/md/dm-clone-target.c-341-\tif (!bio_triggers_commit(clone, bio)) {\ndrivers/md/dm-clone-target.c:342:\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm-clone-target.c-343-\t\treturn;\n--\ndrivers/md/dm-clone-target.c=455=static void complete_discard_bio(struct clone *clone, struct bio *bio, bool success)\n--\ndrivers/md/dm-clone-target.c-468-\t\t\t nr_regions \u003c\u003c clone-\u003eregion_shift);\ndrivers/md/dm-clone-target.c:469:\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm-clone-target.c-470-\t} else\n--\ndrivers/md/dm-clone-target.c=847=static void hydration_overwrite(struct dm_clone_region_hydration *hd, struct bio *bio)\n--\ndrivers/md/dm-clone-target.c-860-\tatomic_inc(\u0026hd-\u003eclone-\u003ehydrations_in_flight);\ndrivers/md/dm-clone-target.c:861:\tsubmit_bio_noacct(bio);\ndrivers/md/dm-clone-target.c-862-}\n--\ndrivers/md/dm-clone-target.c=1226=static void process_deferred_flush_bios(struct clone *clone)\n--\ndrivers/md/dm-clone-target.c-1268-\t\t} else {\ndrivers/md/dm-clone-target.c:1269:\t\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm-clone-target.c-1270-\t\t}\n--\ndrivers/md/dm-era-target.c=1264=static void process_deferred_bios(struct era *era)\n--\ndrivers/md/dm-era-target.c-1316-\t\t\t\tset_bit(get_block(era, bio), ws-\u003ebits);\ndrivers/md/dm-era-target.c:1317:\t\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm-era-target.c-1318-\t\t}\n--\ndrivers/md/dm-integrity.c=2276=static void dm_integrity_map_continue(struct dm_integrity_io *dio, bool from_map)\n--\ndrivers/md/dm-integrity.c-2467-\ndrivers/md/dm-integrity.c:2468:\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm-integrity.c-2469-\n--\ndrivers/md/dm-integrity.c-2472-\ndrivers/md/dm-integrity.c:2473:\tsubmit_bio_noacct(bio);\ndrivers/md/dm-integrity.c-2474-\n--\ndrivers/md/dm-integrity.c=2774=static void integrity_bio_wait(struct work_struct *w)\n--\ndrivers/md/dm-integrity.c-2786-\t\t\tcase DM_MAPIO_REMAPPED:\ndrivers/md/dm-integrity.c:2787:\t\t\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm-integrity.c-2788-\t\t\t\tfallthrough;\n--\ndrivers/md/dm-mpath.c=685=static void process_queued_bios(struct work_struct *work)\n--\ndrivers/md/dm-mpath.c-722-\t\tcase DM_MAPIO_REMAPPED:\ndrivers/md/dm-mpath.c:723:\t\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm-mpath.c-724-\t\t\tbreak;\n--\ndrivers/md/dm-pcache/backing_dev.c=142=static void req_submit_fn(struct work_struct *work)\n--\ndrivers/md/dm-pcache/backing_dev.c-155-\t\tlist_del_init(\u0026backing_req-\u003enode);\ndrivers/md/dm-pcache/backing_dev.c:156:\t\tsubmit_bio_noacct(\u0026backing_req-\u003ebio);\ndrivers/md/dm-pcache/backing_dev.c-157-\t}\n--\ndrivers/md/dm-pcache/backing_dev.c=160=void backing_dev_req_submit(struct pcache_backing_dev_req *backing_req, bool direct)\n--\ndrivers/md/dm-pcache/backing_dev.c-164-\tif (direct) {\ndrivers/md/dm-pcache/backing_dev.c:165:\t\tsubmit_bio_noacct(\u0026backing_req-\u003ebio);\ndrivers/md/dm-pcache/backing_dev.c-166-\t\treturn;\n--\ndrivers/md/dm-raid1.c=686=static void do_writes(struct mirror_set *ms, struct bio_list *writes)\n--\ndrivers/md/dm-raid1.c-788-\t\t\tmap_bio(get_default_mirror(ms), bio);\ndrivers/md/dm-raid1.c:789:\t\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm-raid1.c-790-\t\t}\n--\ndrivers/md/dm-snap-persistent.c=232=static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, blk_opf_t opf,\n--\ndrivers/md/dm-snap-persistent.c-256-\t * Issue the synchronous I/O from a different thread\ndrivers/md/dm-snap-persistent.c:257:\t * to avoid submit_bio_noacct recursion.\ndrivers/md/dm-snap-persistent.c-258-\t */\n--\ndrivers/md/dm-snap.c=1562=static void flush_bios(struct bio *bio)\n--\ndrivers/md/dm-snap.c-1568-\t\tbio-\u003ebi_next = NULL;\ndrivers/md/dm-snap.c:1569:\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm-snap.c-1570-\t\tbio = n;\n--\ndrivers/md/dm-snap.c=1579=static void retry_origin_bios(struct dm_snapshot *s, struct bio *bio)\n--\ndrivers/md/dm-snap.c-1588-\t\tif (r == DM_MAPIO_REMAPPED)\ndrivers/md/dm-snap.c:1589:\t\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm-snap.c-1590-\t\tbio = n;\n--\ndrivers/md/dm-snap.c=1814=static void start_full_bio(struct dm_snap_pending_exception *pe,\n--\ndrivers/md/dm-snap.c-1829-\ndrivers/md/dm-snap.c:1830:\tsubmit_bio_noacct(bio);\ndrivers/md/dm-snap.c-1831-}\n--\ndrivers/md/dm-vdo/flush.c=460=static void vdo_complete_flush_callback(struct vdo_completion *completion)\n--\ndrivers/md/dm-vdo/flush.c-475-\t\tatomic64_inc(\u0026vdo-\u003estats.flush_out);\ndrivers/md/dm-vdo/flush.c:476:\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm-vdo/flush.c-477-\t}\n--\ndrivers/md/dm-vdo/io-submitter.c=107=static void send_bio_to_device(struct vio *vio, struct bio *bio)\n--\ndrivers/md/dm-vdo/io-submitter.c-114-\tbio_set_dev(bio, vdo_get_backing_device(vdo));\ndrivers/md/dm-vdo/io-submitter.c:115:\tsubmit_bio_noacct(bio);\ndrivers/md/dm-vdo/io-submitter.c-116-}\n--\ndrivers/md/dm-verity-target.c=787=static int verity_map(struct dm_target *ti, struct bio *bio)\n--\ndrivers/md/dm-verity-target.c-824-\ndrivers/md/dm-verity-target.c:825:\tsubmit_bio_noacct(bio);\ndrivers/md/dm-verity-target.c-826-\n--\ndrivers/md/dm-writecache.c=1283=static int writecache_flush_thread(void *data)\n--\ndrivers/md/dm-writecache.c-1309-\t\t\tbio_set_dev(bio, wc-\u003edev-\u003ebdev);\ndrivers/md/dm-writecache.c:1310:\t\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm-writecache.c-1311-\t\t} else {\n--\ndrivers/md/dm-zoned-target.c=116=static int dmz_submit_bio(struct dmz_target *dmz, struct dm_zone *zone,\n--\ndrivers/md/dm-zoned-target.c-141-\trefcount_inc(\u0026bioctx-\u003eref);\ndrivers/md/dm-zoned-target.c:142:\tsubmit_bio_noacct(clone);\ndrivers/md/dm-zoned-target.c-143-\n--\ndrivers/md/dm.c=1367=void dm_submit_bio_remap(struct bio *clone, struct bio *tgt_clone)\n--\ndrivers/md/dm.c-1385-\t\t\t tio-\u003eold_sector);\ndrivers/md/dm.c:1386:\tsubmit_bio_noacct(tgt_clone);\ndrivers/md/dm.c-1387-}\n--\ndrivers/md/dm.c=1956=static void dm_split_and_process_bio(struct mapped_device *md,\n--\ndrivers/md/dm.c-2046-\t/*\ndrivers/md/dm.c:2047:\t * Remainder must be passed to submit_bio_noacct() so it gets handled\ndrivers/md/dm.c-2048-\t * *after* bios already submitted have been completely processed.\n--\ndrivers/md/dm.c-2052-\tbio_inc_remaining(bio);\ndrivers/md/dm.c:2053:\tsubmit_bio_noacct(bio);\ndrivers/md/dm.c-2054-out:\n--\ndrivers/md/dm.c=2126=static int dm_poll_bio(struct bio *bio, struct io_comp_batch *iob,\n--\ndrivers/md/dm.c-2143-\t * bio_poll() is only possible once @bio has been completely\ndrivers/md/dm.c:2144:\t * submitted via submit_bio_noacct()'s depth-first submission.\ndrivers/md/dm.c-2145-\t * So there is no dm_queue_poll_io() race associated with\n--\ndrivers/md/dm.c=2846=static void dm_wq_work(struct work_struct *work)\n--\ndrivers/md/dm.c-2858-\ndrivers/md/dm.c:2859:\t\tsubmit_bio_noacct(bio);\ndrivers/md/dm.c-2860-\t\tcond_resched();\n--\ndrivers/md/md-linear.c=235=static bool linear_make_request(struct mddev *mddev, struct bio *bio)\n--\ndrivers/md/md-linear.c-281-\t\tmddev_check_write_zeroes(mddev, bio);\ndrivers/md/md-linear.c:282:\t\tsubmit_bio_noacct(bio);\ndrivers/md/md-linear.c-283-\t}\n--\ndrivers/md/md.c=9348=void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,\n--\ndrivers/md/md.c-9359-\tmddev_trace_remap(mddev, discard_bio, bio-\u003ebi_iter.bi_sector);\ndrivers/md/md.c:9360:\tsubmit_bio_noacct(discard_bio);\ndrivers/md/md.c-9361-}\n--\ndrivers/md/raid0.c=557=static void raid0_map_submit_bio(struct mddev *mddev, struct bio *bio)\n--\ndrivers/md/raid0.c-591-\tmddev_check_write_zeroes(mddev, bio);\ndrivers/md/raid0.c:592:\tsubmit_bio_noacct(bio);\ndrivers/md/raid0.c-593-}\n--\ndrivers/md/raid1-10.c=112=static inline void raid1_submit_write(struct bio *bio)\n--\ndrivers/md/raid1-10.c-124-\telse\ndrivers/md/raid1-10.c:125:\t\tsubmit_bio_noacct(bio);\ndrivers/md/raid1-10.c-126-}\n--\ndrivers/md/raid1.c=1336=static void raid1_read_request(struct mddev *mddev, struct bio *bio,\n--\ndrivers/md/raid1.c-1448-\tmddev_trace_remap(mddev, read_bio, r1_bio-\u003esector);\ndrivers/md/raid1.c:1449:\tsubmit_bio_noacct(read_bio);\ndrivers/md/raid1.c-1450-\treturn;\n--\ndrivers/md/raid1.c=2370=static void sync_request_write(struct mddev *mddev, struct r1bio *r1_bio)\n--\ndrivers/md/raid1.c-2417-\ndrivers/md/raid1.c:2418:\t\tsubmit_bio_noacct(wbio);\ndrivers/md/raid1.c-2419-\t}\n--\ndrivers/md/raid1.c=2801=static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr,\n--\ndrivers/md/raid1.c-3079-\t\t\t\t\tbio-\u003ebi_opf \u0026= ~MD_FAILFAST;\ndrivers/md/raid1.c:3080:\t\t\t\tsubmit_bio_noacct(bio);\ndrivers/md/raid1.c-3081-\t\t\t}\n--\ndrivers/md/raid1.c-3087-\t\t\tbio-\u003ebi_opf \u0026= ~MD_FAILFAST;\ndrivers/md/raid1.c:3088:\t\tsubmit_bio_noacct(bio);\ndrivers/md/raid1.c-3089-\t}\n--\ndrivers/md/raid10.c=1148=static void raid10_read_request(struct mddev *mddev, struct bio *bio,\n--\ndrivers/md/raid10.c-1246-\tmddev_trace_remap(mddev, read_bio, r10_bio-\u003esector);\ndrivers/md/raid10.c:1247:\tsubmit_bio_noacct(read_bio);\ndrivers/md/raid10.c-1248-\treturn;\n--\ndrivers/md/raid10.c=1616=static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)\n--\ndrivers/md/raid10.c-1693-\t\t/* Resend the fist split part */\ndrivers/md/raid10.c:1694:\t\tsubmit_bio_noacct(split);\ndrivers/md/raid10.c-1695-\t\twait_barrier(conf, false);\n--\ndrivers/md/raid10.c-1712-\t\t/* Resend the second split part */\ndrivers/md/raid10.c:1713:\t\tsubmit_bio_noacct(bio);\ndrivers/md/raid10.c-1714-\t\tbio = split;\n--\ndrivers/md/raid10.c=2362=static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)\n--\ndrivers/md/raid10.c-2452-\t\ttbio-\u003ebi_iter.bi_sector += conf-\u003emirrors[d].rdev-\u003edata_offset;\ndrivers/md/raid10.c:2453:\t\tsubmit_bio_noacct(tbio);\ndrivers/md/raid10.c-2454-\t}\n--\ndrivers/md/raid10.c-2466-\t\tatomic_inc(\u0026r10_bio-\u003eremaining);\ndrivers/md/raid10.c:2467:\t\tsubmit_bio_noacct(tbio);\ndrivers/md/raid10.c-2468-\t}\n--\ndrivers/md/raid10.c=2568=static void recovery_request_write(struct mddev *mddev, struct r10bio *r10_bio)\n--\ndrivers/md/raid10.c-2575-\t/* Need to test wbio2-\u003ebi_end_io before we call\ndrivers/md/raid10.c:2576:\t * submit_bio_noacct as if the former is NULL,\ndrivers/md/raid10.c-2577-\t * the latter is free to free wbio2.\n--\ndrivers/md/raid10.c-2597-\t\tatomic_inc(\u0026conf-\u003emirrors[d].rdev-\u003enr_pending);\ndrivers/md/raid10.c:2598:\t\tsubmit_bio_noacct(wbio);\ndrivers/md/raid10.c-2599-\t}\n--\ndrivers/md/raid10.c-2601-\t\tatomic_inc(\u0026conf-\u003emirrors[d].replacement-\u003enr_pending);\ndrivers/md/raid10.c:2602:\t\tsubmit_bio_noacct(wbio2);\ndrivers/md/raid10.c-2603-\t}\n--\ndrivers/md/raid10.c=3104=static void raid10_set_cluster_sync_high(struct r10conf *conf)\n--\ndrivers/md/raid10.c-3159- * which we then process collectively to add pages, and then process again\ndrivers/md/raid10.c:3160: * to pass to submit_bio_noacct.\ndrivers/md/raid10.c-3161- *\n--\ndrivers/md/raid10.c=3169=static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,\n--\ndrivers/md/raid10.c-3725-\t\t\tbio-\u003ebi_status = 0;\ndrivers/md/raid10.c:3726:\t\t\tsubmit_bio_noacct(bio);\ndrivers/md/raid10.c-3727-\t\t}\n--\ndrivers/md/raid10.c=4582=static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,\n--\ndrivers/md/raid10.c-4836-\tread_bio-\u003ebi_next = NULL;\ndrivers/md/raid10.c:4837:\tsubmit_bio_noacct(read_bio);\ndrivers/md/raid10.c-4838-\tsectors_done += nr_sectors;\n--\ndrivers/md/raid10.c=4858=static void reshape_request_write(struct mddev *mddev, struct r10bio *r10_bio)\n--\ndrivers/md/raid10.c-4896-\t\tb-\u003ebi_next = NULL;\ndrivers/md/raid10.c:4897:\t\tsubmit_bio_noacct(b);\ndrivers/md/raid10.c-4898-\t}\n--\ndrivers/md/raid5.c=1033=static void dispatch_bio_list(struct bio_list *tmp)\n--\ndrivers/md/raid5.c-1037-\twhile ((bio = bio_list_pop(tmp)))\ndrivers/md/raid5.c:1038:\t\tsubmit_bio_noacct(bio);\ndrivers/md/raid5.c-1039-}\n--\ndrivers/md/raid5.c=1157=static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)\n--\ndrivers/md/raid5.c-1314-\t\t\telse\ndrivers/md/raid5.c:1315:\t\t\t\tsubmit_bio_noacct(bi);\ndrivers/md/raid5.c-1316-\t\t}\n--\ndrivers/md/raid5.c-1354-\t\t\telse\ndrivers/md/raid5.c:1355:\t\t\t\tsubmit_bio_noacct(rbi);\ndrivers/md/raid5.c-1356-\t\t}\n--\ndrivers/md/raid5.c=5438=static int raid5_read_one_chunk(struct mddev *mddev, struct bio *raid_bio)\n--\ndrivers/md/raid5.c-5509-\tmddev_trace_remap(mddev, align_bio, raid_bio-\u003ebi_iter.bi_sector);\ndrivers/md/raid5.c:5510:\tsubmit_bio_noacct(align_bio);\ndrivers/md/raid5.c-5511-\treturn 1;\n--\ndrivers/nvme/host/multipath.c=508=static void nvme_ns_head_submit_bio(struct bio *bio)\n--\ndrivers/nvme/host/multipath.c-529-\t\t * Use BIO_REMAPPED to skip bio_check_eod() when this bio\ndrivers/nvme/host/multipath.c:530:\t\t * enters submit_bio_noacct() for the per-path device. The EOD\ndrivers/nvme/host/multipath.c-531-\t\t * check already passed on the multipath head.\n--\ndrivers/nvme/host/multipath.c-536-\t\t\t\t bio-\u003ebi_iter.bi_sector);\ndrivers/nvme/host/multipath.c:537:\t\tsubmit_bio_noacct(bio);\ndrivers/nvme/host/multipath.c-538-\t} else if (nvme_available_path(head)) {\n--\ndrivers/nvme/host/multipath.c=673=static void nvme_requeue_work(struct work_struct *work)\n--\ndrivers/nvme/host/multipath.c-686-\ndrivers/nvme/host/multipath.c:687:\t\tsubmit_bio_noacct(bio);\ndrivers/nvme/host/multipath.c-688-\t}\n--\ninclude/linux/blkdev.h=1033=extern void blk_unregister_queue(struct gendisk *disk);\ninclude/linux/blkdev.h:1034:void submit_bio_noacct(struct bio *bio);\ninclude/linux/blkdev.h-1035-struct bio *bio_split_to_limits(struct bio *bio);\n"
} |
| 9/2 | 2026/08/11 12:01 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 31695 output: 22 thoughts: 16 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Revisiting `blk_revalidate_disk_zones`** Alright, time to circle back to `blk_revalidate_disk_zones`. I know we've reviewed this before, and it's a critical piece of the puzzle, especially with the latest changes to zone-aware storage. I need to make sure I haven't missed any edge cases, particularly around error handling and how we're dealing with transient failures during revalidation. Are we still gracefully handling scenarios where zones become temporarily unavailable? What about interactions with other block layer features, like TRIM or wear leveling, and how they might impact the revalidation process? I want to confirm that the existing logic remains sound and that any new code doesn't introduce regressions. The nuances of zone-based disks can be tricky, and a thorough re-examination is always warranted to ensure robust and predictable behavior. Let's dig in and make sure everything is shipshape. |
| 10/2 | 2026/08/11 12:01 | tool | grepper |
0mArgs:{
"Expression": "blk_revalidate_disk_zones"
}Results: {
"Output": "block/blk-zoned.c=627=static bool disk_insert_zone_wplug(struct gendisk *disk,\n--\nblock/blk-zoned.c-652-\t * attached to the disk, then this is a zone write plug insert from the\nblock/blk-zoned.c:653:\t * first call to blk_revalidate_disk_zones(), in which case the zone is\nblock/blk-zoned.c-654-\t * necessarilly in the active condition.\n--\nblock/blk-zoned.c=2427=static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,\n--\nblock/blk-zoned.c-2491-/**\nblock/blk-zoned.c:2492: * blk_revalidate_disk_zones - (re)allocate and initialize zone write plugs\nblock/blk-zoned.c-2493- * @disk:\tTarget disk\n--\nblock/blk-zoned.c-2503- */\nblock/blk-zoned.c:2504:int blk_revalidate_disk_zones(struct gendisk *disk)\nblock/blk-zoned.c-2505-{\n--\nblock/blk-zoned.c-2565-}\nblock/blk-zoned.c:2566:EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);\nblock/blk-zoned.c-2567-\n--\ndrivers/block/null_blk/zoned.c=175=int null_register_zoned_dev(struct nullb *nullb)\n--\ndrivers/block/null_blk/zoned.c-183-\ndrivers/block/null_blk/zoned.c:184:\treturn blk_revalidate_disk_zones(disk);\ndrivers/block/null_blk/zoned.c-185-}\n--\ndrivers/block/ublk_drv.c=559=static int ublk_get_nr_zones(const struct ublk_device *ub)\n--\ndrivers/block/ublk_drv.c-566-\ndrivers/block/ublk_drv.c:567:static int ublk_revalidate_disk_zones(struct ublk_device *ub)\ndrivers/block/ublk_drv.c-568-{\ndrivers/block/ublk_drv.c:569:\treturn blk_revalidate_disk_zones(ub-\u003eub_disk);\ndrivers/block/ublk_drv.c-570-}\n--\ndrivers/block/ublk_drv.c=770=static void ublk_dev_param_zoned_apply(struct ublk_device *ub)\n--\ndrivers/block/ublk_drv.c-773-\ndrivers/block/ublk_drv.c:774:static int ublk_revalidate_disk_zones(struct ublk_device *ub)\ndrivers/block/ublk_drv.c-775-{\n--\ndrivers/block/ublk_drv.c=4447=static int ublk_ctrl_start_dev(struct ublk_device *ub,\n--\ndrivers/block/ublk_drv.c-4571-\tif (ublk_dev_is_zoned(ub)) {\ndrivers/block/ublk_drv.c:4572:\t\tret = ublk_revalidate_disk_zones(ub);\ndrivers/block/ublk_drv.c-4573-\t\tif (ret)\n--\ndrivers/block/virtio_blk.c=1438=static int virtblk_probe(struct virtio_device *vdev)\n--\ndrivers/block/virtio_blk.c-1537-\t (lim.features \u0026 BLK_FEAT_ZONED)) {\ndrivers/block/virtio_blk.c:1538:\t\terr = blk_revalidate_disk_zones(vblk-\u003edisk);\ndrivers/block/virtio_blk.c-1539-\t\tif (err)\n--\ndrivers/block/zloop.c=1185=static int zloop_ctl_add(struct zloop_options *opts)\n--\ndrivers/block/zloop.c-1326-\ndrivers/block/zloop.c:1327:\tret = blk_revalidate_disk_zones(zlo-\u003edisk);\ndrivers/block/zloop.c-1328-\tif (ret)\n--\ndrivers/md/dm-zone.c=150=bool dm_is_zone_write(struct mapped_device *md, struct bio *bio)\n--\ndrivers/md/dm-zone.c-168- * for zone append emulation. Note that we cannot simply use the block layer\ndrivers/md/dm-zone.c:169: * blk_revalidate_disk_zones() function here as the mapped device is suspended\ndrivers/md/dm-zone.c-170- * (this is called from __bind() context).\n--\ndrivers/md/dm-zone.c=172=int dm_revalidate_zones(struct dm_table *t, struct request_queue *q)\n--\ndrivers/md/dm-zone.c-198-\tmd-\u003erevalidate_map_task = current;\ndrivers/md/dm-zone.c:199:\tret = blk_revalidate_disk_zones(disk);\ndrivers/md/dm-zone.c-200-\tmd-\u003erevalidate_map_task = NULL;\n--\ndrivers/nvme/host/core.c=2387=static int nvme_update_ns_info_block(struct nvme_ns *ns,\n--\ndrivers/nvme/host/core.c-2499-\tif (blk_queue_is_zoned(ns-\u003equeue)) {\ndrivers/nvme/host/core.c:2500:\t\tret = blk_revalidate_disk_zones(ns-\u003edisk);\ndrivers/nvme/host/core.c-2501-\t\tif (ret \u0026\u0026 !nvme_first_scan(ns-\u003edisk))\n--\ndrivers/scsi/sd_zbc.c=519=static void sd_zbc_print_zones(struct scsi_disk *sdkp)\n--\ndrivers/scsi/sd_zbc.c-536-/*\ndrivers/scsi/sd_zbc.c:537: * Call blk_revalidate_disk_zones() if any of the zoned disk properties have\ndrivers/scsi/sd_zbc.c-538- * changed that make it necessary to call that function. Called by\n--\ndrivers/scsi/sd_zbc.c=541=int sd_zbc_revalidate_zones(struct scsi_disk *sdkp)\n--\ndrivers/scsi/sd_zbc.c-565-\tflags = memalloc_noio_save();\ndrivers/scsi/sd_zbc.c:566:\tret = blk_revalidate_disk_zones(disk);\ndrivers/scsi/sd_zbc.c-567-\tmemalloc_noio_restore(flags);\n--\ninclude/linux/blkdev.h=459=int blkdev_zone_mgmt(struct block_device *bdev, enum req_op op,\ninclude/linux/blkdev.h-460-\t\tsector_t sectors, sector_t nr_sectors);\ninclude/linux/blkdev.h:461:int blk_revalidate_disk_zones(struct gendisk *disk);\ninclude/linux/blkdev.h-462-\n"
} |
| 11/2 | 2026/08/11 12:01 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 33802 output: 22 thoughts: 136 Thoughts: **Zone Management Function Overview** Okay, let's break this down. It seems we have a few key functions related to zone management in the block layer, and I need to understand their scope and how they're used. First, `blk_revalidate_disk_zones` is out there, exported and utilized by both `null_blk` and `ublk`. That indicates this is a lower level function used by those device drivers. Now, `bdev_check_zone_mgmt` is also exported, but specifically called within `submit_bio_noacct` in `block/blk-core.c`. This suggests that `bdev_check_zone_mgmt` is central to the core block layer operations. The real point of interest is the two ioctl-related functions: `blkdev_get_zone_info` and `blkdev_zone_mgmt_ioctl`. `blkdev_get_zone_info` is tied to `blkdev_report_zones_ioctl` which suggests this might be reporting zone information to userspace. Also, `blkdev_zone_mgmt_ioctl` is specifically for zone management which indicates that it is a control interface. This means that if I want to report and manage the zones, I should focus on these two functions. |
| 12/2 | 2026/08/11 12:01 | tool | grepper |
0mArgs:{
"Expression": "blkdev_get_zone_info"
}Results: {
"Output": "block/blk-zoned.c=1073=static inline bool blkdev_has_cached_report_zones(struct block_device *bdev)\n--\nblock/blk-zoned.c-1080-/**\nblock/blk-zoned.c:1081: * blkdev_get_zone_info - Get a single zone information from cached data\nblock/blk-zoned.c-1082- * @bdev: Target block device\n--\nblock/blk-zoned.c-1094- */\nblock/blk-zoned.c:1095:int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,\nblock/blk-zoned.c-1096-\t\t\t struct blk_zone *zone)\n--\nblock/blk-zoned.c-1187-}\nblock/blk-zoned.c:1188:EXPORT_SYMBOL_GPL(blkdev_get_zone_info);\nblock/blk-zoned.c-1189-\n--\nblock/blk-zoned.c-1200- * device driver to get the zone report from the device, use\nblock/blk-zoned.c:1201: * blkdev_get_zone_info() to generate the report from the disk zone write\nblock/blk-zoned.c-1202- * plugs and zones condition array. Since calling this function without a\n--\nblock/blk-zoned.c=1205=int blkdev_report_zones_cached(struct block_device *bdev, sector_t sector,\n--\nblock/blk-zoned.c-1234-\t sector += zone_sectors, idx++) {\nblock/blk-zoned.c:1235:\t\tret = blkdev_get_zone_info(bdev, sector, \u0026zone);\nblock/blk-zoned.c-1236-\t\tif (ret)\n--\nfs/xfs/xfs_zone_alloc.c=996=xfs_query_write_pointer(\n--\nfs/xfs/xfs_zone_alloc.c-1008-\tif (bdev_is_zoned(bdev)) {\nfs/xfs/xfs_zone_alloc.c:1009:\t\terror = blkdev_get_zone_info(bdev, start, \u0026zone);\nfs/xfs/xfs_zone_alloc.c-1010-\t\tif (error)\n--\ninclude/linux/blkdev.h=448=int disk_report_zone(struct gendisk *disk, struct blk_zone *zone,\n--\ninclude/linux/blkdev.h-450-\ninclude/linux/blkdev.h:451:int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,\ninclude/linux/blkdev.h-452-\t\t\t struct blk_zone *zone);\n"
} |
| 13/2 | 2026/08/11 12:01 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 34952 output: 22 thoughts: 44 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Tracing Zone Reporting: A Quick Mental Walkthrough** Okay, so I'm seeing `blkdev_get_zone_info` get called by `blkdev_report_zones_cached`, which, in turn, is leveraged by `blkdev_report_zones_ioctl`. This looks like a standard flow for zone reporting, but I'll quickly verify the call chain in my head to make sure I'm not missing any potential side effects or conditional paths. I want to confirm that the data retrieval and caching are correctly managed before it's presented via ioctl. The flow is as expected for zone management. |
| 14/2 | 2026/08/11 12:01 | tool | grepper |
0mArgs:{
"Expression": "blkdev_report_zones_cached"
}Results: {
"Output": "block/blk-zoned.c=492=int blkdev_report_zones_ioctl(struct block_device *bdev, unsigned int cmd,\n--\nblock/blk-zoned.c-521-\t\t\treturn -EINVAL;\nblock/blk-zoned.c:522:\t\tret = blkdev_report_zones_cached(bdev, rep.sector, rep.nr_zones,\nblock/blk-zoned.c-523-\t\t\t\t\t blkdev_copy_zone_to_user, \u0026args);\n--\nblock/blk-zoned.c=1188=EXPORT_SYMBOL_GPL(blkdev_get_zone_info);\n--\nblock/blk-zoned.c-1190-/**\nblock/blk-zoned.c:1191: * blkdev_report_zones_cached - Get cached zones information\nblock/blk-zoned.c-1192- * @bdev: Target block device\n--\nblock/blk-zoned.c-1204- */\nblock/blk-zoned.c:1205:int blkdev_report_zones_cached(struct block_device *bdev, sector_t sector,\nblock/blk-zoned.c-1206-\t\t\tunsigned int nr_zones, report_zones_cb cb, void *data)\n--\nblock/blk-zoned.c-1245-}\nblock/blk-zoned.c:1246:EXPORT_SYMBOL_GPL(blkdev_report_zones_cached);\nblock/blk-zoned.c-1247-\n--\nfs/btrfs/zoned.c=226=static int btrfs_get_dev_zones(struct btrfs_device *device, u64 pos,\n--\nfs/btrfs/zoned.c-270-\nfs/btrfs/zoned.c:271:\tret = blkdev_report_zones_cached(device-\u003ebdev, pos \u003e\u003e SECTOR_SHIFT,\nfs/btrfs/zoned.c-272-\t\t\t\t\t *nr_zones, copy_zone_info_cb, zones);\n--\nfs/btrfs/zoned.c=891=int btrfs_sb_log_location_bdev(struct block_device *bdev, int mirror, int rw,\n--\nfs/btrfs/zoned.c-919-\nfs/btrfs/zoned.c:920:\tret = blkdev_report_zones_cached(bdev, zone_start_sector(sb_zone, bdev),\nfs/btrfs/zoned.c-921-\t\t\t\t\t BTRFS_NR_SB_LOG_ZONES,\n--\ninclude/linux/blkdev.h=455=int blkdev_report_zones(struct block_device *bdev, sector_t sector,\ninclude/linux/blkdev.h-456-\t\tunsigned int nr_zones, report_zones_cb cb, void *data);\ninclude/linux/blkdev.h:457:int blkdev_report_zones_cached(struct block_device *bdev, sector_t sector,\ninclude/linux/blkdev.h-458-\t\tunsigned int nr_zones, report_zones_cb cb, void *data);\n"
} |
| 15/2 | 2026/08/11 12:01 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 35957 output: 22 thoughts: 54 Thoughts: Okay, here's my take on those thoughts, framed as my own mental processing: **Delving into Block Device Zone Management** Alright, let's see, I'm tracing through this block device zone code. I see that `blkdev_report_zones_ioctl` relies on `blkdev_report_zones_cached`. That's a familiar pattern, caching the results to avoid unnecessary work. Now, I'm also noticing `blkdev_zone_mgmt_ioctl` mentioned as another ioctl related to zones. I'm going to take a closer look at `blkdev_zone_mgmt_ioctl` and see what that one does. It's time to dig into the details of this second zone management ioctl. |
| 16/2 | 2026/08/11 12:01 | tool | grepper |
0mArgs:{
"Expression": "blkdev_zone_mgmt_ioctl"
}Results: {
"Output": "block/blk-zoned.c=539=static int blkdev_reset_zone(struct block_device *bdev, blk_mode_t mode,\n--\nblock/blk-zoned.c-570- */\nblock/blk-zoned.c:571:int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,\nblock/blk-zoned.c-572-\t\t\t unsigned int cmd, unsigned long arg)\n--\nblock/blk.h=576=int blkdev_report_zones_ioctl(struct block_device *bdev, unsigned int cmd,\nblock/blk.h-577-\t\tunsigned long arg);\nblock/blk.h:578:int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,\nblock/blk.h-579-\t\tunsigned int cmd, unsigned long arg);\n--\nblock/blk.h=613=static inline int blkdev_report_zones_ioctl(struct block_device *bdev,\n--\nblock/blk.h-617-}\nblock/blk.h:618:static inline int blkdev_zone_mgmt_ioctl(struct block_device *bdev,\nblock/blk.h-619-\t\tblk_mode_t mode, unsigned int cmd, unsigned long arg)\n--\nblock/ioctl.c=651=static int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode,\n--\nblock/ioctl.c-676-\tcase BLKFINISHZONE:\nblock/ioctl.c:677:\t\treturn blkdev_zone_mgmt_ioctl(bdev, mode, cmd, arg);\nblock/ioctl.c-678-\tcase BLKGETZONESZ:\n"
} |
| 17/2 | 2026/08/11 12:01 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 36649 output: 22 thoughts: 321 Thoughts: **Analyzing Zone Management in the Block Layer** Okay, so I'm looking at how zone management is handled in the block layer. From the existing code, it looks like `blkdev_zone_mgmt_ioctl` and `blkdev_report_zones_ioctl` are the main ioctls involved – those are definitely going into my `FocusSymbols` list. I'll also add `blk_revalidate_disk_zones`; it seems like a crucial function for ensuring zone consistency. Now, I initially thought `bdev_check_zone_mgmt` was a possible inclusion, but it's called from `submit_bio_noacct`, which is usually a hot path, so I was hesitant. Wait, hold on. Looking closer at the code, `bdev_check_zone_mgmt` *only* gets called within `submit_bio_noacct` when handling specific zone management operations. Specifically: `REQ_OP_ZONE_OPEN`, `REQ_OP_ZONE_CLOSE`, `REQ_OP_ZONE_RESET`, and `REQ_OP_ZONE_FINISH`. So it's *not* on the performance-critical path for standard reads and writes. That's good to know. Consequently, `bdev_check_zone_mgmt` will be added to `FocusSymbols`. Now, I should check `disk_zone_wplug_abort` to confirm it is also required. |
| 18/2 | 2026/08/11 12:01 | tool | grepper |
0mArgs:{
"Expression": "disk_zone_wplug_abort"
}Results: {
"Output": "block/blk-zoned.c=756=static inline void blk_zone_wplug_bio_io_error(struct blk_zone_wplug *zwplug,\n--\nblock/blk-zoned.c-770- */\nblock/blk-zoned.c:771:static void disk_zone_wplug_abort(struct blk_zone_wplug *zwplug)\nblock/blk-zoned.c-772-{\n--\nblock/blk-zoned.c=818=static inline bool disk_check_zone_wplug_dead(struct blk_zone_wplug *zwplug)\n--\nblock/blk-zoned.c-820-\tif (disk_zone_wplug_is_offline_or_readonly(zwplug)) {\nblock/blk-zoned.c:821:\t\tdisk_zone_wplug_abort(zwplug);\nblock/blk-zoned.c-822-\t\tdisk_mark_zone_wplug_dead(zwplug);\n--\nblock/blk-zoned.c=931=static void disk_zone_wplug_set_wp_offset(struct gendisk *disk,\n--\nblock/blk-zoned.c-943-\nblock/blk-zoned.c:944:\tdisk_zone_wplug_abort(zwplug);\nblock/blk-zoned.c-945-\tif (!zwplug-\u003ewp_offset || disk_zone_wplug_is_full(disk, zwplug))\n--\nblock/blk-zoned.c=1694=static void blk_zone_wplug_handle_native_zone_append(struct bio *bio)\n--\nblock/blk-zoned.c-1729-\t\t\t\t disk-\u003edisk_name, zwplug-\u003ezone_no);\nblock/blk-zoned.c:1730:\t\tdisk_zone_wplug_abort(zwplug);\nblock/blk-zoned.c-1731-\t}\n--\nblock/blk-zoned.c=1830=void blk_zone_write_plug_bio_endio(struct bio *bio)\n--\nblock/blk-zoned.c-1858-\t\tspin_lock_irqsave(\u0026zwplug-\u003elock, flags);\nblock/blk-zoned.c:1859:\t\tdisk_zone_wplug_abort(zwplug);\nblock/blk-zoned.c-1860-\t\tzwplug-\u003eflags |= BLK_ZONE_WPLUG_NEED_WP_UPDATE;\n--\nblock/blk-zoned.c=2280=static void disk_drop_zone_wplug(struct blk_zone_wplug *zwplug, void *data)\n--\nblock/blk-zoned.c-2284-\tspin_lock_irqsave(\u0026zwplug-\u003elock, flags);\nblock/blk-zoned.c:2285:\tdisk_zone_wplug_abort(zwplug);\nblock/blk-zoned.c-2286-\tdisk_mark_zone_wplug_dead(zwplug);\n"
} |
| 19/2 | 2026/08/11 12:01 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 38036 output: 21 thoughts: 27 Thoughts: Here's my take on those thoughts: **Analyzing `disk_zone_wplug_abort`** Okay, so `disk_zone_wplug_abort` is declared as static. This immediately tells me it's likely scoped to a specific file or module, and isn't intended for broader external use. This could mean it's an internal helper function within a disk management component. Now, I need to understand its relationship to the larger picture, so my next logical step is to dig into `disk_alloc_zone_resources`. Let's see what's going on there. I'm guessing that function is where the resources this abort function relates to are provisioned. Knowing how that resource allocation works will tell me more about where this abort function comes into play. |
| 20/2 | 2026/08/11 12:01 | tool | grepper |
0mArgs:{
"Expression": "disk_alloc_zone_resources"
}Results: {
"Output": "block/blk-zoned.c=2025=static unsigned int disk_get_nr_zones(struct gendisk *disk)\n--\nblock/blk-zoned.c-2043-\nblock/blk-zoned.c:2044:static int disk_alloc_zone_resources(struct gendisk *disk)\nblock/blk-zoned.c-2045-{\n--\nblock/blk-zoned.c=2504=int blk_revalidate_disk_zones(struct gendisk *disk)\n--\nblock/blk-zoned.c-2528-\tif (disk_need_zone_resources(disk) \u0026\u0026 !disk-\u003ezone_wplugs_hash) {\nblock/blk-zoned.c:2529:\t\tret = disk_alloc_zone_resources(disk);\nblock/blk-zoned.c-2530-\t\tif (ret) {\n"
} |
| 21/2 | 2026/08/11 12:01 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 38474 output: 21 thoughts: 26 Thoughts: Here's a summary of my mental process: **Diving into Disk Allocation Resources** Alright, let's see... the `disk_alloc_zone_resources` function—that's static, isn't it? Good to keep that in mind. Now, I need to understand how these resources are actually initialized. The next logical step is to examine the `disk_init_zone_resources` function. That should give me the crucial context I'm after, revealing the setup and initial state of these allocation zones. I expect to find the building blocks for the allocation strategy in there. |
| 22/2 | 2026/08/11 12:01 | tool | grepper |
0mArgs:{
"Expression": "disk_init_zone_resources"
}Results: {
"Output": "block/blk-zoned.c=1967=static int disk_zone_wplugs_worker(void *data)\n--\nblock/blk-zoned.c-2015-\nblock/blk-zoned.c:2016:void disk_init_zone_resources(struct gendisk *disk)\nblock/blk-zoned.c-2017-{\n--\nblock/blk.h=524=static inline void ioc_clear_queue(struct request_queue *q)\n--\nblock/blk.h-529-#ifdef CONFIG_BLK_DEV_ZONED\nblock/blk.h:530:void disk_init_zone_resources(struct gendisk *disk);\nblock/blk.h-531-void disk_release_zone_resources(struct gendisk *disk);\n--\nblock/blk.h=580=bool bdev_check_zone_mgmt(struct block_device *bdev, sector_t sector);\nblock/blk.h-581-#else /* CONFIG_BLK_DEV_ZONED */\nblock/blk.h:582:static inline void disk_init_zone_resources(struct gendisk *disk)\nblock/blk.h-583-{\n--\nblock/genhd.c=1453=struct gendisk *__alloc_disk_node(struct request_queue *q, int node_id,\n--\nblock/genhd.c-1484-\nblock/genhd.c:1485:\tdisk_init_zone_resources(disk);\nblock/genhd.c-1486-\trand_initialize_disk(disk);\n"
} |
| 23/2 | 2026/08/11 12:01 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 39050 output: 21 thoughts: 34 Thoughts: Here's my analysis of the provided code snippet, thinking like an expert: **Investigating Disk Resource Management** Okay, so I see that `disk_init_zone_resources` is called from `__alloc_disk_node`. My first thought is to understand the lifecycle of these resources. Allocation and initialization go hand-in-hand here, as expected, but I need to make sure I understand the corresponding deallocation process. To get a full picture, I need to understand when and how these zone resources are released. That means I need to dig into `disk_release_zone_resources`. Let's see what that function does and where it's called. This is key to preventing resource leaks and ensuring proper cleanup when a disk node is no longer needed. |
| 24/2 | 2026/08/11 12:01 | tool | grepper |
0mArgs:{
"Expression": "disk_release_zone_resources"
}Results: {
"Output": "block/blk-zoned.c=2144=static void disk_set_zones_state_array(struct gendisk *disk, u8 *zones_state)\n--\nblock/blk-zoned.c-2155-\nblock/blk-zoned.c:2156:void disk_release_zone_resources(struct gendisk *disk)\nblock/blk-zoned.c-2157-{\n--\nblock/blk.h=530=void disk_init_zone_resources(struct gendisk *disk);\nblock/blk.h:531:void disk_release_zone_resources(struct gendisk *disk);\nblock/blk.h-532-static inline bool bio_zone_write_plugging(struct bio *bio)\n--\nblock/blk.h=582=static inline void disk_init_zone_resources(struct gendisk *disk)\n--\nblock/blk.h-584-}\nblock/blk.h:585:static inline void disk_release_zone_resources(struct gendisk *disk)\nblock/blk.h-586-{\n--\nblock/genhd.c=1279=static void disk_release(struct device *dev)\n--\nblock/genhd.c-1309-\tkfree(disk-\u003erandom);\nblock/genhd.c:1310:\tdisk_release_zone_resources(disk);\nblock/genhd.c-1311-\txa_destroy(\u0026disk-\u003epart_tbl);\n"
} |
| 25/2 | 2026/08/11 12:01 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 39638 output: 24 thoughts: 33 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating Resource Release in the Disk Subsystem** Alright, let's see... I'm looking at `disk_release_zone_resources` and it's called from `disk_release`. That's standard and expected, the cleanup after a disk is, well, released. But that raises a related question, and I'm sure I'm not the first one to ask this... What's the relationship, if any, between this and `blk_zone_wplug_handle_write`? I know that function is responsible for handling write plug operations within zoned block devices. We're dealing with freeing resources related to zoned devices and writes. It seems like it's a good place to start investigating how those two related functions interact and the exact nature of their relationship. I need to understand whether this resource release is correctly coordinated with any write plug operations. I need to consider potential race conditions and ensure data integrity. Gotta check how these functions deal with zoned writes on this device. |
| 26/2 | 2026/08/11 12:01 | tool | grepper |
0mArgs:{
"Expression": "blk_zone_wplug_handle_write"
}Results: {
"Output": "block/blk-zoned.c=1526=static bool blk_zone_wplug_prepare_bio(struct blk_zone_wplug *zwplug,\n--\nblock/blk-zoned.c-1583-\nblock/blk-zoned.c:1584:static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs)\nblock/blk-zoned.c-1585-{\n--\nblock/blk-zoned.c=1750=bool blk_zone_plug_bio(struct bio *bio, unsigned int nr_segs)\n--\nblock/blk-zoned.c-1782-\tcase REQ_OP_WRITE_ZEROES:\nblock/blk-zoned.c:1783:\t\treturn blk_zone_wplug_handle_write(bio, nr_segs);\nblock/blk-zoned.c-1784-\tdefault:\n"
} |
| 27/2 | 2026/08/11 12:01 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 40166 output: 21 thoughts: 12 Thoughts: Here's a summary of my immediate thought process: **Initial Assessment: Understanding the Significance of `blk_zone_plug_bio`** Okay, so `blk_zone_plug_bio` is exported. That's the key starting point. Immediately, my mind starts to race. If it's *exported*, it's likely a function, a variable, or a data structure available outside of its defining module. This means it's designed for interaction, for use by other parts of the system – potentially in ways I need to understand for this analysis. My next line of thinking: Why is it exported? What purpose does it serve? What part of the block layer is it related to? The name itself gives clues: "blk" suggests "block," "zone" implies a region or section, "plug" hints at connection or intervention, and "bio" is the standard abbreviation for a bio request within the kernel. Is this related to zone block devices like SMR HDDs, or some other storage type? Could it relate to error handling, queue management, or some other aspect of the block layer's interaction with the underlying storage? The fact that it's *exported* makes this immediately interesting, as if I'm not careful, it could be the cause of bugs I'm debugging. Is it used often? I need to dig deeper. I need to understand its function signature, its intended use cases, and how it fits into the broader picture of the block layer's architecture. I'll need to check the kernel source code, likely using `grep` or `cscope`, or whatever tools are handy at the moment. Understanding the context of its export is crucial, because this could potentially open up for a variety of use cases, or indicate a specific design choice that may be impacting the system. Depending on the scenario, I need to know where and how the function is used in order to properly understand it. |
| 28/2 | 2026/08/11 12:02 | tool | grepper |
0mArgs:{
"Expression": "blk_zone_plug_bio"
}Results: {
"Output": "block/blk-mq.c=3093=void blk_mq_submit_bio(struct bio *bio)\n--\nblock/blk-mq.c-3158-\tif (bio_needs_zone_write_plugging(bio)) {\nblock/blk-mq.c:3159:\t\tif (blk_zone_plug_bio(bio, nr_segs))\nblock/blk-mq.c-3160-\t\t\tgoto queue_exit;\n--\nblock/blk-zoned.c=1694=static void blk_zone_wplug_handle_native_zone_append(struct bio *bio)\n--\nblock/blk-zoned.c-1738-/**\nblock/blk-zoned.c:1739: * blk_zone_plug_bio - Handle a zone write BIO with zone write plugging\nblock/blk-zoned.c-1740- * @bio: The BIO being submitted\n--\nblock/blk-zoned.c-1749- */\nblock/blk-zoned.c:1750:bool blk_zone_plug_bio(struct bio *bio, unsigned int nr_segs)\nblock/blk-zoned.c-1751-{\n--\nblock/blk-zoned.c-1787-}\nblock/blk-zoned.c:1788:EXPORT_SYMBOL_GPL(blk_zone_plug_bio);\nblock/blk-zoned.c-1789-\n--\ndrivers/md/dm.c=1819=static inline bool dm_zone_plug_bio(struct mapped_device *md, struct bio *bio)\n--\ndrivers/md/dm.c-1822-\t\treturn false;\ndrivers/md/dm.c:1823:\treturn blk_zone_plug_bio(bio, 0);\ndrivers/md/dm.c-1824-}\n--\ninclude/linux/blkdev.h=872=static inline unsigned int disk_nr_zones(struct gendisk *disk)\n--\ninclude/linux/blkdev.h-882- * Return true whenever @bio execution needs to be handled through zone\ninclude/linux/blkdev.h:883: * write plugging (using blk_zone_plug_bio()). Return false otherwise.\ninclude/linux/blkdev.h-884- */\ninclude/linux/blkdev.h=885=static inline bool bio_needs_zone_write_plugging(struct bio *bio)\n--\ninclude/linux/blkdev.h-909-\t * All zone write operations must be handled through zone write plugging\ninclude/linux/blkdev.h:910:\t * using blk_zone_plug_bio().\ninclude/linux/blkdev.h-911-\t */\n--\ninclude/linux/blkdev.h-924-\ninclude/linux/blkdev.h:925:bool blk_zone_plug_bio(struct bio *bio, unsigned int nr_segs);\ninclude/linux/blkdev.h-926-\n--\ninclude/linux/blkdev.h=963=static inline bool bio_needs_zone_write_plugging(struct bio *bio)\n--\ninclude/linux/blkdev.h-967-\ninclude/linux/blkdev.h:968:static inline bool blk_zone_plug_bio(struct bio *bio, unsigned int nr_segs)\ninclude/linux/blkdev.h-969-{\n"
} |
| 29/2 | 2026/08/11 12:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 41450 output: 22 thoughts: 478 Thoughts: **My Analysis of Potential Hot Path Bottlenecks in Zoned Block Device Operations** Okay, so I'm looking at `blk_zone_plug_bio`, which I know is exported and used in `blk_mq_submit_bio` and `dm_zone_plug_bio`. These are definitely hot paths, so any performance issues here would be significant. I need to pinpoint the relevant functions and start digging. First, I've got to focus my attention on these key functions: `blk_revalidate_disk_zones`, `blkdev_report_zones_ioctl`, `blkdev_zone_mgmt_ioctl`, and `bdev_check_zone_mgmt`. I'm starting here because I know these are called in this context. Now, let me check the details of `blkdev_report_zones_ioctl`. I want to see if it's exported or if it’s static. I've found it defined in `block/blk-zoned.c` and declared in `block/blk.h`. Looks like it is in `block/ioctl.c` in `blkdev_common_ioctl` which means it is reachable from user space via `ioctl` on block devices. This is a very useful find. Next, I need to look at `disk_zone_wplug_abort`. It's static, which is too bad, but it is called from `disk_check_zone_wplug_dead`, `disk_zone_wplug_set_wp_offset`, `blk_zone_wplug_handle_native_zone_append`, `blk_zone_write_plug_bio_endio`, and `disk_drop_zone_wplug`. So, while it's a good target in terms of functionality, it's not directly accessible for my purposes. Then I checked `disk_alloc_zone_resources`, and that's also static. I'll need to remember this for later consideration. Now, what about `disk_init_zone_resources`? Is it exported? No, it looks to be in `block/blk-zoned.c` and declared in `block/blk.h` and called from `__alloc_disk_node`. This one is related to initialization, so this is another one for later. Similarly, I looked at `disk_release_zone_resources`. It's defined in `block/blk-zoned.c` and declared in `block/blk.h` and is called from `disk_release`. Another one to keep in mind, but not the priority for now. So, I will focus on these, as I have determined that they are exported: - `blk_revalidate_disk_zones` - `blkdev_report_zones_ioctl` - `blkdev_zone_mgmt_ioctl` - `bdev_check_zone_mgmt` Finally, let me double-check the exact name of `blkdev_report_zones_ioctl`. Gotta be sure I got that one right. |
| 30/2 | 2026/08/11 12:02 | tool | grepper |
0mArgs:{
"Expression": "blkdev_report_zones_ioctl"
}Results: {
"Output": "block/blk-zoned.c=473=static int blkdev_copy_zone_to_user(struct blk_zone *zone, unsigned int idx,\n--\nblock/blk-zoned.c-491- */\nblock/blk-zoned.c:492:int blkdev_report_zones_ioctl(struct block_device *bdev, unsigned int cmd,\nblock/blk-zoned.c-493-\t\tunsigned long arg)\n--\nblock/blk.h=571=static inline void blk_zone_finish_request(struct request *rq)\n--\nblock/blk.h-575-}\nblock/blk.h:576:int blkdev_report_zones_ioctl(struct block_device *bdev, unsigned int cmd,\nblock/blk.h-577-\t\tunsigned long arg);\n--\nblock/blk.h=610=static inline void blk_zone_finish_request(struct request *rq)\n--\nblock/blk.h-612-}\nblock/blk.h:613:static inline int blkdev_report_zones_ioctl(struct block_device *bdev,\nblock/blk.h-614-\t\tunsigned int cmd, unsigned long arg)\n--\nblock/ioctl.c=651=static int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode,\n--\nblock/ioctl.c-671-\tcase BLKREPORTZONEV2:\nblock/ioctl.c:672:\t\treturn blkdev_report_zones_ioctl(bdev, cmd, arg);\nblock/ioctl.c-673-\tcase BLKRESETZONE:\n"
} |
| 31/2 | 2026/08/11 12:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 43018 output: 22 thoughts: 32 Thoughts: Here's my thought process, summarized as you requested: **Navigating Block Device Zone Management** Okay, so we're talking about block devices and zone management, right? The key function I need to confirm is definitely `blkdev_report_zones_ioctl`. That's the one I'm pretty certain is directly involved here. I'll need to double check that name to ensure I'm getting the right function. Now let's just make absolutely sure by checking `blkdev_zone_mgmt_ioctl` too. It's better to be too thorough than to miss a critical piece. |
| 32/2 | 2026/08/11 12:02 | tool | grepper |
0mArgs:{
"Expression": "blkdev_zone_mgmt_ioctl"
}Results: {
"Output": "block/blk-zoned.c=539=static int blkdev_reset_zone(struct block_device *bdev, blk_mode_t mode,\n--\nblock/blk-zoned.c-570- */\nblock/blk-zoned.c:571:int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,\nblock/blk-zoned.c-572-\t\t\t unsigned int cmd, unsigned long arg)\n--\nblock/blk.h=576=int blkdev_report_zones_ioctl(struct block_device *bdev, unsigned int cmd,\nblock/blk.h-577-\t\tunsigned long arg);\nblock/blk.h:578:int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,\nblock/blk.h-579-\t\tunsigned int cmd, unsigned long arg);\n--\nblock/blk.h=613=static inline int blkdev_report_zones_ioctl(struct block_device *bdev,\n--\nblock/blk.h-617-}\nblock/blk.h:618:static inline int blkdev_zone_mgmt_ioctl(struct block_device *bdev,\nblock/blk.h-619-\t\tblk_mode_t mode, unsigned int cmd, unsigned long arg)\n--\nblock/ioctl.c=651=static int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode,\n--\nblock/ioctl.c-676-\tcase BLKFINISHZONE:\nblock/ioctl.c:677:\t\treturn blkdev_zone_mgmt_ioctl(bdev, mode, cmd, arg);\nblock/ioctl.c-678-\tcase BLKGETZONESZ:\n"
} |
| 33/2 | 2026/08/11 12:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 43659 output: 22 thoughts: 32 Thoughts: Okay, here's my attempt at summarizing those thoughts from a first-person, expert perspective, focusing on the streamlined thought process: **Identifying the Target and Preliminary Validation** Yes, that's it, without a doubt, `blkdev_zone_mgmt_ioctl`. That name rings a bell, I’m familiar with it. My focus is confirmed. Now, to make sure I'm on the right track, let's just quickly check `bdev_check_zone_mgmt`. That should be the perfect verification step to start with. |
| 34/2 | 2026/08/11 12:02 | tool | grepper |
0mArgs:{
"Expression": "bdev_check_zone_mgmt"
}Results: {
"Output": "block/blk-core.c=817=void submit_bio_noacct(struct bio *bio)\n--\nblock/blk-core.c-907-\t\t */\nblock/blk-core.c:908:\t\tif (!bdev_check_zone_mgmt(bdev, bio-\u003ebi_iter.bi_sector))\nblock/blk-core.c-909-\t\t\tgoto end_io;\n--\nblock/blk-zoned.c=309=EXPORT_SYMBOL_GPL(bdev_zone_is_seq);\n--\nblock/blk-zoned.c-311-/**\nblock/blk-zoned.c:312: * bdev_check_zone_mgmt - check if a sector belongs to a valid sequential zone\nblock/blk-zoned.c-313- * @bdev: block device to check\n--\nblock/blk-zoned.c-318- */\nblock/blk-zoned.c:319:bool bdev_check_zone_mgmt(struct block_device *bdev, sector_t sector)\nblock/blk-zoned.c-320-{\n--\nblock/blk.h=578=int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,\nblock/blk.h-579-\t\tunsigned int cmd, unsigned long arg);\nblock/blk.h:580:bool bdev_check_zone_mgmt(struct block_device *bdev, sector_t sector);\nblock/blk.h-581-#else /* CONFIG_BLK_DEV_ZONED */\n--\nblock/blk.h=618=static inline int blkdev_zone_mgmt_ioctl(struct block_device *bdev,\n--\nblock/blk.h-622-}\nblock/blk.h:623:static inline bool bdev_check_zone_mgmt(struct block_device *bdev,\nblock/blk.h-624-\t\t\t\t\tsector_t sector)\n"
} |
| 35/2 | 2026/08/11 12:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 44323 output: 22 thoughts: 32 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Navigating the Zone Management Landscape** Right, `bdev_check_zone_mgmt`... yes, that's precisely the function I'm looking for in this context. It's the key starting point for verifying our zone management configuration. Now, let's drill down into the details. Time to examine how the zone information is actually being retrieved and handled. I need to refresh my memory on the underlying data structures and access methods. Let's dig into `blkdev_get_zone_info` and see what it's telling us. I want to confirm our current assumptions. |
| 36/2 | 2026/08/11 12:02 | tool | grepper |
0mArgs:{
"Expression": "blkdev_get_zone_info"
}Results: {
"Output": "block/blk-zoned.c=1073=static inline bool blkdev_has_cached_report_zones(struct block_device *bdev)\n--\nblock/blk-zoned.c-1080-/**\nblock/blk-zoned.c:1081: * blkdev_get_zone_info - Get a single zone information from cached data\nblock/blk-zoned.c-1082- * @bdev: Target block device\n--\nblock/blk-zoned.c-1094- */\nblock/blk-zoned.c:1095:int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,\nblock/blk-zoned.c-1096-\t\t\t struct blk_zone *zone)\n--\nblock/blk-zoned.c-1187-}\nblock/blk-zoned.c:1188:EXPORT_SYMBOL_GPL(blkdev_get_zone_info);\nblock/blk-zoned.c-1189-\n--\nblock/blk-zoned.c-1200- * device driver to get the zone report from the device, use\nblock/blk-zoned.c:1201: * blkdev_get_zone_info() to generate the report from the disk zone write\nblock/blk-zoned.c-1202- * plugs and zones condition array. Since calling this function without a\n--\nblock/blk-zoned.c=1205=int blkdev_report_zones_cached(struct block_device *bdev, sector_t sector,\n--\nblock/blk-zoned.c-1234-\t sector += zone_sectors, idx++) {\nblock/blk-zoned.c:1235:\t\tret = blkdev_get_zone_info(bdev, sector, \u0026zone);\nblock/blk-zoned.c-1236-\t\tif (ret)\n--\nfs/xfs/xfs_zone_alloc.c=996=xfs_query_write_pointer(\n--\nfs/xfs/xfs_zone_alloc.c-1008-\tif (bdev_is_zoned(bdev)) {\nfs/xfs/xfs_zone_alloc.c:1009:\t\terror = blkdev_get_zone_info(bdev, start, \u0026zone);\nfs/xfs/xfs_zone_alloc.c-1010-\t\tif (error)\n--\ninclude/linux/blkdev.h=448=int disk_report_zone(struct gendisk *disk, struct blk_zone *zone,\n--\ninclude/linux/blkdev.h-450-\ninclude/linux/blkdev.h:451:int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,\ninclude/linux/blkdev.h-452-\t\t\t struct blk_zone *zone);\n"
} |
| 37/2 | 2026/08/11 12:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 45244 output: 23 thoughts: 140 Thoughts: **Analyzing Zoned Block Device Functions** Okay, so I'm honing in on the functions related to zoned block device management. Right, the function name I was looking for is indeed `blkdev_get_zone_info`. That's the one. Now, let's nail down the critical functions. I should focus on these: `blk_revalidate_disk_zones`, `blkdev_report_zones_ioctl`, `blkdev_zone_mgmt_ioctl`, `bdev_check_zone_mgmt`, and, of course, the one I mentioned already - `blkdev_get_zone_info`. These are clearly the core components for zone management operations. Also, I'll need to check the relevant configurations. The obvious one is `BLK_DEV_ZONED`, obviously, that would define all the code related to zoned devices. And then there is the `BLK_DEV_NULL_BLK`. That's for those testing environments, virtual ones, where you need to simulate this stuff. I'm going to want to see how these are set in Kconfig. Gotta make sure those settings are understood completely. |
| 38/2 | 2026/08/11 12:02 | 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"
} |
| 39/2 | 2026/08/11 12:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 45814 output: 23 thoughts: 13 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in the weeds of kernel development and understand the implications of `BLK_DEV_NULL_BLK`: **Diving into the Null Block Device** Alright, time to get my hands dirty. My immediate thought is, "Let's check `BLK_DEV_NULL_BLK`". I'm thinking about the null block device. This likely indicates I'm troubleshooting or digging into the storage stack. Maybe there's a problem with a device driver, or I'm analyzing how a block device is being handled, or perhaps I'm looking for a performance bottleneck somewhere in the I/O pipeline. Grabbing that macro suggests that I'm specifically interested in how a "null" or "black hole" block device is behaving. I need to understand its characteristics, its role within the kernel, its interaction with the underlying drivers, and possibly trace its use to identify the root cause of the issue. The name itself indicates this is a key component. I'm going to follow the logic and implications related to this device. |
| 40/2 | 2026/08/11 12:02 | 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"
} |
| 41/2 | 2026/08/11 12:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 46225 output: 25 thoughts: 45 Thoughts: Okay, here's my take on those thoughts, framed as an expert's inner monologue: **Considering Block Device Definitions** Right, let's nail down what we're going to include. I definitely need to factor in `BLK_DEV_ZONED` and `BLK_DEV_NULL_BLK`. Those are fundamental to the discussion we're having. Hmm, and I'm also thinking `BLK_DEV_ZONED_LOOP` could be relevant here, depending on how specific the context of this discussion will become. It may add to the completeness of the explanation. I should quickly check to see if that's right. |
| 42/2 | 2026/08/11 12:02 | 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"
} |
| 43/2 | 2026/08/11 12:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 46529 output: 34 thoughts: 24 Thoughts: Here's my thought process, as if I were considering this myself: **Investigating Block Device Zoned Loop Configuration** Okay, so I need to figure out the configuration options related to the zoned block device loop, specifically where they are defined. I'm pretty certain that the Kconfig files are the place to start. With that in mind, I know the source is typically within the `drivers/block` directory. Within that I'm specifically looking for `Kconfig`, which would make the most sense, so I will now check `drivers/block/Kconfig`. |
| 44/2 | 2026/08/11 12:02 | 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"
} |
| 45/2 | 2026/08/11 12:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 47069 output: 22 thoughts: 50 Thoughts: **Considering Block Device Configuration Options** Okay, yes, definitely including `BLK_DEV_ZONED_LOOP` seems like a solid choice. That feels right. And, on reflection, `BLK_DEV_UBLK` also might come in handy here. It warrants further investigation. I'm going to quickly check `ublk_drv.c` to see precisely how it handles zoned block devices. A quick skim of that source file will solidify whether it's truly relevant to the current project objectives. |
| 46/2 | 2026/08/11 12:02 | tool | grepper |
0mArgs:{
"Expression": "config BLK_DEV_UBLK"
}Results: {
"Output": "drivers/block/Kconfig=315=config BLK_DEV_RBD\n--\ndrivers/block/Kconfig-327-\ndrivers/block/Kconfig:328:config BLK_DEV_UBLK\ndrivers/block/Kconfig-329-\ttristate \"Userspace block driver (Experimental)\"\n"
} |
| 47/2 | 2026/08/11 12:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 47338 output: 279 thoughts: 323 Thoughts: **My Assessment of the Zoned Block Device Patch** Okay, so I've been reviewing this patch, and here's my take. It's focused on improving the block layer's handling of zoned block devices, which is critical. I'm seeing a shift in how zone states are managed; specifically, the new `enum blk_zstate` replacing the old `zones_cond` array with `zones_state`. That's a solid architectural improvement for clarity and maintainability. It seems like the core issue is improved handling of zoned block devices. The introduction of `bdev_check_zone_mgmt` to validate sectors during zone management is a very welcome addition. Ensuring that operations don't target offline or read-only zones is fundamental to stability. The modifications to zone write plug handling, especially aborting plugged BIOs for those problematic zones, is also key to preventing data corruption. Since we're dealing with block layer logic, and this patch touches zoned block device functionality, it's pretty clear that it has the potential to impact the behavior of devices emulated via `null_blk`, `zloop`, and `ublk`. The fact that it's functional and reachable makes it a prime candidate for fuzzing. I'll need to pay close attention to the behavior of these emulated devices. The list of `FocusSymbols` seems right on target, especially `bdev_check_zone_mgmt`, since that's a new and central piece. The others, `blk_revalidate_disk_zones`, `blkdev_report_zones_ioctl`, `blkdev_zone_mgmt_ioctl`, and `blkdev_get_zone_info`, are all important, too, as they are related to interactions and information about these zones. The `EnableConfigs`—`BLK_DEV_ZONED`, `BLK_DEV_NULL_BLK`, `BLK_DEV_ZONED_LOOP`, and `BLK_DEV_UBLK`—are what I'd expect. These are the configurations that enable the relevant functionality, and these options will make all the fuzzing and testing possible. Alright, I'm confident with this, time to call set-results. |
| 48/2 | 2026/08/11 12:03 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"BLK_DEV_ZONED",
"BLK_DEV_NULL_BLK",
"BLK_DEV_ZONED_LOOP",
"BLK_DEV_UBLK"
],
"FocusSymbols": [
"blk_revalidate_disk_zones",
"blkdev_report_zones_ioctl",
"blkdev_zone_mgmt_ioctl",
"bdev_check_zone_mgmt",
"blkdev_get_zone_info"
],
"Reasoning": "The patch modifies the core block layer's handling of zoned block devices. It introduces a new `enum blk_zstate` to combine zone condition and zone type, replacing the previous `zones_cond` array with `zones_state`. It adds `bdev_check_zone_mgmt` to validate sectors for zone management operations, ensuring they are not offline or read-only. It also modifies zone write plug handling, including aborting plugged BIOs for offline/read-only zones. These changes affect core block layer logic for zoned block devices, which can be reached via software-emulated zoned devices like `null_blk`, `zloop`, or `ublk`. The changes are functional and reachable, so they are worth fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"BLK_DEV_ZONED",
"BLK_DEV_NULL_BLK",
"BLK_DEV_ZONED_LOOP",
"BLK_DEV_UBLK"
],
"FocusSymbols": [
"blk_revalidate_disk_zones",
"blkdev_report_zones_ioctl",
"blkdev_zone_mgmt_ioctl",
"bdev_check_zone_mgmt",
"blkdev_get_zone_info"
],
"Reasoning": "The patch modifies the core block layer's handling of zoned block devices. It introduces a new `enum blk_zstate` to combine zone condition and zone type, replacing the previous `zones_cond` array with `zones_state`. It adds `bdev_check_zone_mgmt` to validate sectors for zone management operations, ensuring they are not offline or read-only. It also modifies zone write plug handling, including aborting plugged BIOs for offline/read-only zones. These changes affect core block layer logic for zoned block devices, which can be reached via software-emulated zoned devices like `null_blk`, `zloop`, or `ublk`. The changes are functional and reachable, so they are worth fuzzing.",
"WorthFuzzing": true
} |
| 49/1 | 2026/08/11 12:03 | action | If |
1mArgs:{
"WorthFuzzing": true
}Results: null |
| 50/2 | 2026/08/11 12:03 | agent | kmsan-evaluator |
1mModel:best-expensive Args: null Results: {
"KMSANReasoning": "The patch refactors how zone conditions are stored internally in the block layer, replacing the `zones_cond` array with a `zones_state` array that packs both zone condition and zone type into a single byte. It also adds checks to prevent issuing zone management commands or writes to offline or read-only zones.\n\nAll newly introduced or modified memory allocations for these internal states (e.g., `args-\u003ezones_state` in `disk_init_revalidate_args`) use `kzalloc`, ensuring they are fully zero-initialized. The `struct blk_zone` structure, which is eventually copied to user space via `blkdev_copy_zone_to_user`, is allocated on the stack in `blkdev_report_zones_cached` but is fully zeroed out using `memset(zone, 0, sizeof(*zone))` in `blkdev_get_zone_info` before any fields are populated. \n\nSince all relevant structures and arrays are explicitly zero-initialized, there is no risk of uninitialized memory reads or kernel-to-user info-leaks. Any potential bugs introduced by this patch (such as out-of-bounds accesses, use-after-free, or logic errors) would be effectively caught by standard KASAN, LOCKDEP, and other default bug detectors. Therefore, a dedicated KMSAN fuzzing session is not required.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
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 490015abd3b7223e282686e5da557289054de9c9
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Aug 11 12:00:36 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..02f92d113d324 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 = BLK_ZFLAG_CONV;
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);
@@ -827,6 +980,12 @@ static unsigned int disk_zone_wplug_sync_wp_offset(struct gendisk *disk,
unsigned long flags;
spin_lock_irqsave(&zwplug->lock, flags);
+ if (disk_zone_cond_is_offline_or_readonly(zone->cond)) {
+ zwplug->flags &= ~BLK_ZONE_WPLUG_NEED_WP_UPDATE;
+ zwplug->cond = zone->cond;
+ zwplug->wp_offset = UINT_MAX;
+ disk_mark_zone_wplug_dead(zwplug);
+ }
if (zwplug->flags & BLK_ZONE_WPLUG_NEED_WP_UPDATE)
disk_zone_wplug_set_wp_offset(disk, zwplug, wp_offset);
spin_unlock_irqrestore(&zwplug->lock, flags);
@@ -871,7 +1030,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 +1099,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 +1114,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 +1135,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 +1146,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 +1270,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 +1541,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 +1603,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 +2015,23 @@ 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;
+
+ if (!lim->chunk_sectors)
+ return 0;
+
+ 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 +2041,24 @@ 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, pool_size, i;
int ret = -ENOMEM;
- atomic_set(&disk->nr_zone_wplugs, 0);
+ nr_zones = disk_get_nr_zones(disk);
+ if (!nr_zones)
+ return -ENODEV;
+
+ /*
+ * 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 +2076,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 +2086,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 +2141,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 +2161,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 +2184,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 +2211,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_set_zones_state_array(disk, args->zones_state);
+ args->zones_state = NULL;
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;
/*
* Some devices can advertise zone resource limits that are larger than
@@ -2120,48 +2272,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) || !nr_zones)
+ 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 +2332,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 +2367,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 +2409,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 +2467,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 +2503,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 +2547,19 @@ 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);
+ disk->nr_zones = 0;
return ret;
}
@@ -2435,8 +2610,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 +2637,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;
|
| 51/3 | 2026/08/11 12:03 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17779 output: 28 thoughts: 607 Thoughts: **Analyzing the Block Layer Zone State Patch for Uninitialized Memory** Okay, so I'm looking at this patch series that tweaks the block layer, specifically around zone management. It seems we're dealing with `blk-core.c`, `blk-zoned.c`, `blk.h`, `genhd.c`, and `blkdev.h`. The core of the change is this new internal representation, `enum blk_zstate`, for zone conditions, and the replacement of `zones_cond` with `zones_state` within `struct gendisk`. It looks like they're trying to compact the storage of these zone conditions, squeezing them into 4 bits and using the higher-order bits for some zone type flags, like `BLK_ZFLAG_CONV`. They've also added a bunch of helper functions like `blk_zstate_to_zone_cond` and `disk_zone_get_state` to handle the conversion and access. My immediate concern, as always, is uninitialized memory. Let's see... the `zones_state` array is allocated in `disk_init_revalidate_args`: ```c /* Cached zone conditions: 1 byte per zone */ args->zones_state = kzalloc(args->nr_zones, GFP_NOIO); if (!args->zones_state) return -ENOMEM; ``` Ah, good, it uses `kzalloc`. That's important. So the memory is zero-initialized. That's a huge win in preventing initial garbage. Now, this patch modifies functions like `blkdev_get_zone_info` and `blk_revalidate_disk_zones`. In `blkdev_get_zone_info`: ```c zs = zones_state[disk_zone_no(disk, sector)]; zone->cond = blk_zstate_to_zone_cond(zs); ``` Okay, `zones_state` is fully initialized by `kzalloc` and updated later by `blk_zstate_set`. So, the read should always be valid. Okay, let's step back and consider if user-space structures are involved. `struct blk_zone` is the obvious one, used in ioctls like `BLKREPORTZONE`. I see the patch modifies `blkdev_get_zone_info`, which populates `struct blk_zone`. ```c 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; ``` Now, is this going to leave any fields in `struct blk_zone` uninitialized? `blkdev_get_zone_info` is populating `zone->start`, `zone->len`, `zone->capacity`, `zone->wp`, `zone->type`, and `zone->cond`. But, wait... does this struct have other fields that are left untouched? I should check the `struct blk_zone` definition again just to be sure. It would be easy to miss something and leave a field uninitialized, leading to a potential vulnerability. I need to be thorough here. |
| 52/3 | 2026/08/11 12:03 | tool | grepper |
0mArgs:{
"Expression": "struct blk_zone \\{",
"PathPrefix": "include/"
}Results: {
"Output": "include/uapi/linux/blkzoned.h=99=enum blk_zone_report_flags {\n--\ninclude/uapi/linux/blkzoned.h-128- */\ninclude/uapi/linux/blkzoned.h:129:struct blk_zone {\ninclude/uapi/linux/blkzoned.h-130-\t__u64\tstart;\t\t/* Zone start sector */\n"
} |
| 53/3 | 2026/08/11 12:03 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19255 output: 40 thoughts: 15 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into `struct blk_zone`** Okay, let's get down to brass tacks. I need the full, unabridged definition of `struct blk_zone`. This isn't just a casual glance; I need *everything*. The goal is to deeply understand this structure and all its implications within the block layer's zoned storage context. I need to know precisely how it's defined, what fields it contains, and most critically, how those fields relate to the zoned block device paradigm. I'm going to look for any hidden nuances or implications in the structure's layout that aren't immediately obvious, potentially looking at it from a performance-aware perspective as well. Is it packed, aligned in any particularly interesting way? Are there any fields that jump out as potential bottlenecks or areas for optimization? Let's get the full source code and dissect it carefully. No stone unturned. |
| 54/3 | 2026/08/11 12:03 | tool | read-file |
0mArgs:{
"File": "include/uapi/linux/blkzoned.h",
"FirstLine": 129,
"LineCount": 20
}Results: {
"Contents": " 129:\tstruct blk_zone {\n 130:\t\t__u64\tstart;\t\t/* Zone start sector */\n 131:\t\t__u64\tlen;\t\t/* Zone length in number of sectors */\n 132:\t\t__u64\twp;\t\t/* Zone write pointer position */\n 133:\t\t__u8\ttype;\t\t/* Zone type */\n 134:\t\t__u8\tcond;\t\t/* Zone condition */\n 135:\t\t__u8\tnon_seq;\t/* Non-sequential write resources active */\n 136:\t\t__u8\treset;\t\t/* Reset write pointer recommended */\n 137:\t\t__u8\tresv[4];\n 138:\t\t__u64\tcapacity;\t/* Zone capacity in number of sectors */\n 139:\t\t__u8\treserved[24];\n 140:\t};\n 141:\t\n 142:\t/**\n 143:\t * struct blk_zone_report - BLKREPORTZONE ioctl request/reply\n 144:\t *\n 145:\t * @sector: starting sector of report\n 146:\t * @nr_zones: IN maximum / OUT actual\n 147:\t * @flags: one or more flags as defined by enum blk_zone_report_flags.\n 148:\t * @flags: one or more flags as defined by enum blk_zone_report_flags.\n"
} |
| 55/3 | 2026/08/11 12:03 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19889 output: 28 thoughts: 50 Thoughts: Here's my take on the thought process, summarized as you requested: **Deconstructing a Kernel Patch** Okay, so this patch is touching `blkdev_get_zone_info`. Right off the bat, that function name looks familiar, but my internal alarm bells are going off... `blkdev_get_zone_info` - isn't that an internal function? It's responsible for populating `struct blk_zone`, which is critical for zoned block device management. I need to confirm this quickly. I need to figure out where this function is actually called from and what the impact of this change will be. Time to dig into the call sites. |
| 56/3 | 2026/08/11 12:03 | tool | grepper |
0mArgs:{
"Expression": "blkdev_get_zone_info",
"PathPrefix": ""
}Results: {
"Output": "block/blk-zoned.c=1073=static inline bool blkdev_has_cached_report_zones(struct block_device *bdev)\n--\nblock/blk-zoned.c-1080-/**\nblock/blk-zoned.c:1081: * blkdev_get_zone_info - Get a single zone information from cached data\nblock/blk-zoned.c-1082- * @bdev: Target block device\n--\nblock/blk-zoned.c-1094- */\nblock/blk-zoned.c:1095:int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,\nblock/blk-zoned.c-1096-\t\t\t struct blk_zone *zone)\n--\nblock/blk-zoned.c-1187-}\nblock/blk-zoned.c:1188:EXPORT_SYMBOL_GPL(blkdev_get_zone_info);\nblock/blk-zoned.c-1189-\n--\nblock/blk-zoned.c-1200- * device driver to get the zone report from the device, use\nblock/blk-zoned.c:1201: * blkdev_get_zone_info() to generate the report from the disk zone write\nblock/blk-zoned.c-1202- * plugs and zones condition array. Since calling this function without a\n--\nblock/blk-zoned.c=1205=int blkdev_report_zones_cached(struct block_device *bdev, sector_t sector,\n--\nblock/blk-zoned.c-1234-\t sector += zone_sectors, idx++) {\nblock/blk-zoned.c:1235:\t\tret = blkdev_get_zone_info(bdev, sector, \u0026zone);\nblock/blk-zoned.c-1236-\t\tif (ret)\n--\nfs/xfs/xfs_zone_alloc.c=996=xfs_query_write_pointer(\n--\nfs/xfs/xfs_zone_alloc.c-1008-\tif (bdev_is_zoned(bdev)) {\nfs/xfs/xfs_zone_alloc.c:1009:\t\terror = blkdev_get_zone_info(bdev, start, \u0026zone);\nfs/xfs/xfs_zone_alloc.c-1010-\t\tif (error)\n--\ninclude/linux/blkdev.h=448=int disk_report_zone(struct gendisk *disk, struct blk_zone *zone,\n--\ninclude/linux/blkdev.h-450-\ninclude/linux/blkdev.h:451:int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,\ninclude/linux/blkdev.h-452-\t\t\t struct blk_zone *zone);\n"
} |
| 57/3 | 2026/08/11 12:03 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20828 output: 28 thoughts: 38 Thoughts: Here's a summary of my thought process: **Diving into Block Device Zone Reporting** Okay, so I see `blkdev_get_zone_info` is being used within `blkdev_report_zones_cached`. My immediate thought is, "Right, let's take a look at *that* function, `blkdev_report_zones_cached`." It seems like a logical next step to understand how the zone information is being retrieved and utilized in this particular context. I need to understand the relationship between the retrieval and the reporting mechanisms. I suspect that `blkdev_report_zones_cached` likely takes the information retrieved by `blkdev_get_zone_info` and somehow formats or presents it. |
| 58/3 | 2026/08/11 12:03 | tool | grepper |
0mArgs:{
"Expression": "blkdev_report_zones_cached",
"PathPrefix": ""
}Results: {
"Output": "block/blk-zoned.c=492=int blkdev_report_zones_ioctl(struct block_device *bdev, unsigned int cmd,\n--\nblock/blk-zoned.c-521-\t\t\treturn -EINVAL;\nblock/blk-zoned.c:522:\t\tret = blkdev_report_zones_cached(bdev, rep.sector, rep.nr_zones,\nblock/blk-zoned.c-523-\t\t\t\t\t blkdev_copy_zone_to_user, \u0026args);\n--\nblock/blk-zoned.c=1188=EXPORT_SYMBOL_GPL(blkdev_get_zone_info);\n--\nblock/blk-zoned.c-1190-/**\nblock/blk-zoned.c:1191: * blkdev_report_zones_cached - Get cached zones information\nblock/blk-zoned.c-1192- * @bdev: Target block device\n--\nblock/blk-zoned.c-1204- */\nblock/blk-zoned.c:1205:int blkdev_report_zones_cached(struct block_device *bdev, sector_t sector,\nblock/blk-zoned.c-1206-\t\t\tunsigned int nr_zones, report_zones_cb cb, void *data)\n--\nblock/blk-zoned.c-1245-}\nblock/blk-zoned.c:1246:EXPORT_SYMBOL_GPL(blkdev_report_zones_cached);\nblock/blk-zoned.c-1247-\n--\nfs/btrfs/zoned.c=226=static int btrfs_get_dev_zones(struct btrfs_device *device, u64 pos,\n--\nfs/btrfs/zoned.c-270-\nfs/btrfs/zoned.c:271:\tret = blkdev_report_zones_cached(device-\u003ebdev, pos \u003e\u003e SECTOR_SHIFT,\nfs/btrfs/zoned.c-272-\t\t\t\t\t *nr_zones, copy_zone_info_cb, zones);\n--\nfs/btrfs/zoned.c=891=int btrfs_sb_log_location_bdev(struct block_device *bdev, int mirror, int rw,\n--\nfs/btrfs/zoned.c-919-\nfs/btrfs/zoned.c:920:\tret = blkdev_report_zones_cached(bdev, zone_start_sector(sb_zone, bdev),\nfs/btrfs/zoned.c-921-\t\t\t\t\t BTRFS_NR_SB_LOG_ZONES,\n--\ninclude/linux/blkdev.h=455=int blkdev_report_zones(struct block_device *bdev, sector_t sector,\ninclude/linux/blkdev.h-456-\t\tunsigned int nr_zones, report_zones_cb cb, void *data);\ninclude/linux/blkdev.h:457:int blkdev_report_zones_cached(struct block_device *bdev, sector_t sector,\ninclude/linux/blkdev.h-458-\t\tunsigned int nr_zones, report_zones_cb cb, void *data);\n"
} |
| 59/3 | 2026/08/11 12:03 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 21840 output: 30 thoughts: 82 Thoughts: Okay, here's my breakdown of this ioctl flow, assuming I'm the one thinking through it: **Deconstructing the Block Device Zone Reporting** Right, so `blkdev_report_zones_ioctl` is the entry point, and it's calling `blkdev_report_zones_cached`. That makes sense; it's likely trying to optimize the common case. `blkdev_report_zones_cached` then hits `blkdev_get_zone_info`, which is where the real work of populating that `struct blk_zone` happens. This is the core data structure containing all the zone details, and I need to ensure the data is accurate. Finally, the information gets handed off to `blkdev_copy_zone_to_user`. I'm going to want to take a closer look at `blkdev_copy_zone_to_user`. I need to be certain that the data is correctly marshaled and that there aren't any potential issues with memory access or unexpected side effects during the copy. I also want to make sure the data passed to the user is secure. |
| 60/3 | 2026/08/11 12:03 | tool | grepper |
0mArgs:{
"Expression": "blkdev_copy_zone_to_user",
"PathPrefix": ""
}Results: {
"Output": "block/blk-zoned.c=469=struct zone_report_args {\n--\nblock/blk-zoned.c-472-\nblock/blk-zoned.c:473:static int blkdev_copy_zone_to_user(struct blk_zone *zone, unsigned int idx,\nblock/blk-zoned.c-474-\t\t\t\t void *data)\n--\nblock/blk-zoned.c=492=int blkdev_report_zones_ioctl(struct block_device *bdev, unsigned int cmd,\n--\nblock/blk-zoned.c-516-\t\tret = blkdev_report_zones(bdev, rep.sector, rep.nr_zones,\nblock/blk-zoned.c:517:\t\t\t\t\t blkdev_copy_zone_to_user, \u0026args);\nblock/blk-zoned.c-518-\t\tbreak;\n--\nblock/blk-zoned.c-522-\t\tret = blkdev_report_zones_cached(bdev, rep.sector, rep.nr_zones,\nblock/blk-zoned.c:523:\t\t\t\t\t blkdev_copy_zone_to_user, \u0026args);\nblock/blk-zoned.c-524-\t\tbreak;\n"
} |
| 61/3 | 2026/08/11 12:03 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 22502 output: 35 thoughts: 16 Thoughts: Okay, let's dive into `blkdev_copy_zone_to_user`. I'm assuming, given the name, this function is responsible for transferring block device data from a specific zone to user space. Immediately, I'm thinking about the implications for performance and security. How are they handling the copy? Is it a straight `memcpy`, or are they employing more sophisticated techniques like DMA or zero-copy? I need to know the details of the transfer mechanism to assess potential bottlenecks and vulnerabilities. I'm also curious about the zone concept. Is this related to a zoned block device, or some other internal partitioning? If it's zoned, are they adhering to the ZBC/ZAC standards? I have to consider the implications of sequential writes and the complexities introduced by zones, especially when dealing with potentially overlapping or out-of-order requests. Security is paramount. What kind of validation is happening on the user-provided zone information? Are they checking the validity of the address range to prevent out-of-bounds reads? Is there any protection against race conditions if multiple user processes are attempting concurrent access? I'll need to look for checks on buffer size, alignment, and permissions, because if there's a vulnerability there, a malicious user could exploit this function to read or manipulate sensitive data from the kernel's memory space. So, I need to look closely at the arguments passed to this function. Then, I need to see the data flow: data source, data destination, size of the data, and security checks. It's likely that a structure containing some block details is being passed in, or maybe just pointers to start and end addresses. I'm going to look for any calls to `copy_to_user` or similar functions, and then trace back from there, looking for validation and sanitization. This is going to be interesting! |
| 62/3 | 2026/08/11 12:03 | tool | read-file |
0mArgs:{
"File": "block/blk-zoned.c",
"FirstLine": 470,
"LineCount": 20
}Results: {
"Contents": " 470:\t\tstruct blk_zone __user *zones;\n 471:\t};\n 472:\t\n 473:\tstatic int blkdev_copy_zone_to_user(struct blk_zone *zone, unsigned int idx,\n 474:\t\t\t\t\t void *data)\n 475:\t{\n 476:\t\tstruct zone_report_args *args = data;\n 477:\t\n 478:\t\tif (copy_to_user(\u0026args-\u003ezones[idx], zone, sizeof(struct blk_zone)))\n 479:\t\t\treturn -EFAULT;\n 480:\t\treturn 0;\n 481:\t}\n 482:\t\n 483:\t/*\n 484:\t * Mask of valid input flags for BLKREPORTZONEV2 ioctl.\n 485:\t */\n 486:\t#define BLK_ZONE_REPV2_INPUT_FLAGS\tBLK_ZONE_REP_CACHED\n 487:\t\n 488:\t/*\n 489:\t * BLKREPORTZONE and BLKREPORTZONEV2 ioctl processing.\n"
} |
| 63/3 | 2026/08/11 12:03 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 23241 output: 36 thoughts: 83 Thoughts: **Analyzing the blkdev_copy_zone_to_user Function** Okay, so I'm looking at `blkdev_copy_zone_to_user`. It seems pretty straightforward: it's designed to copy the entire `struct blk_zone` to user space using `copy_to_user`. My first thought is always, *where* is this `struct blk_zone` allocated? I need to understand the lifecycle of this data structure to be confident about what I'm doing. It looks like it's passed as a pointer to the function, which means the allocation must happen *before* the call to `blkdev_copy_zone_to_user`. Let's dig deeper to see where that structure is being set up. I'll take a look at `blkdev_report_zones_cached` to trace its origin. Understanding its allocation strategy will be key to understanding the usage pattern. |
| 64/3 | 2026/08/11 12:03 | tool | read-file |
0mArgs:{
"File": "block/blk-zoned.c",
"FirstLine": 1205,
"LineCount": 40
}Results: {
"Contents": "1205:\tint blkdev_report_zones_cached(struct block_device *bdev, sector_t sector,\n1206:\t\t\t\tunsigned int nr_zones, report_zones_cb cb, void *data)\n1207:\t{\n1208:\t\tstruct gendisk *disk = bdev-\u003ebd_disk;\n1209:\t\tsector_t capacity = get_capacity(disk);\n1210:\t\tsector_t zone_sectors = bdev_zone_sectors(bdev);\n1211:\t\tunsigned int idx = 0;\n1212:\t\tstruct blk_zone zone;\n1213:\t\tint ret;\n1214:\t\n1215:\t\tif (!cb || !bdev_is_zoned(bdev) ||\n1216:\t\t WARN_ON_ONCE(!disk-\u003efops-\u003ereport_zones))\n1217:\t\t\treturn -EOPNOTSUPP;\n1218:\t\n1219:\t\tif (!nr_zones || sector \u003e= capacity)\n1220:\t\t\treturn 0;\n1221:\t\n1222:\t\tif (!blkdev_has_cached_report_zones(bdev)) {\n1223:\t\t\tstruct blk_report_zones_args args = {\n1224:\t\t\t\t.cb = cb,\n1225:\t\t\t\t.data = data,\n1226:\t\t\t\t.report_active = true,\n1227:\t\t\t};\n1228:\t\n1229:\t\t\treturn blkdev_do_report_zones(bdev, sector, nr_zones, \u0026args);\n1230:\t\t}\n1231:\t\n1232:\t\tfor (sector = bdev_zone_start(bdev, sector);\n1233:\t\t sector \u003c capacity \u0026\u0026 idx \u003c nr_zones;\n1234:\t\t sector += zone_sectors, idx++) {\n1235:\t\t\tret = blkdev_get_zone_info(bdev, sector, \u0026zone);\n1236:\t\t\tif (ret)\n1237:\t\t\t\treturn ret;\n1238:\t\n1239:\t\t\tret = cb(\u0026zone, idx, data);\n1240:\t\t\tif (ret)\n1241:\t\t\t\treturn ret;\n1242:\t\t}\n1243:\t\n1244:\t\treturn idx;\n"
} |
| 65/3 | 2026/08/11 12:03 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 24161 output: 36 thoughts: 86 Thoughts: **Uninitialized Data in Block Device Zone Reporting** Okay, so I'm looking at `blkdev_report_zones_cached` and noticed something potentially problematic. On line 1212, we've got `struct blk_zone zone;` declared *on the stack*. The alarm bells are going off immediately because it's *not* initialized to zero. That's a classic source of unpredictable behavior, depending on what garbage happens to be left over in that stack memory. And the issue is that this uninitialized `zone` variable is then immediately passed into `blkdev_get_zone_info` via its address. We really need to confirm that `blkdev_get_zone_info` properly initializes all members of the `zone` struct. Let's dig into that function definition and see what we're dealing with. I can't let uninitialized memory potentially introduce subtle bugs. |
| 66/3 | 2026/08/11 12:03 | tool | read-file |
0mArgs:{
"File": "block/blk-zoned.c",
"FirstLine": 1095,
"LineCount": 60
}Results: {
"Contents": "1095:\tint blkdev_get_zone_info(struct block_device *bdev, sector_t sector,\n1096:\t\t\t\t struct blk_zone *zone)\n1097:\t{\n1098:\t\tstruct gendisk *disk = bdev-\u003ebd_disk;\n1099:\t\tsector_t zone_sectors = bdev_zone_sectors(bdev);\n1100:\t\tstruct blk_zone_wplug *zwplug;\n1101:\t\tunsigned long flags;\n1102:\t\tu8 *zones_state, zs;\n1103:\t\n1104:\t\tif (!bdev_is_zoned(bdev))\n1105:\t\t\treturn -EOPNOTSUPP;\n1106:\t\n1107:\t\tif (sector \u003e= get_capacity(disk))\n1108:\t\t\treturn -EINVAL;\n1109:\t\n1110:\t\tmemset(zone, 0, sizeof(*zone));\n1111:\t\tsector = bdev_zone_start(bdev, sector);\n1112:\t\n1113:\t\tif (!blkdev_has_cached_report_zones(bdev))\n1114:\t\t\treturn blkdev_report_zone_fallback(bdev, sector, zone);\n1115:\t\n1116:\t\trcu_read_lock();\n1117:\t\tzones_state = rcu_dereference(disk-\u003ezones_state);\n1118:\t\tif (!disk-\u003ezone_wplugs_hash || !zones_state) {\n1119:\t\t\trcu_read_unlock();\n1120:\t\t\treturn blkdev_report_zone_fallback(bdev, sector, zone);\n1121:\t\t}\n1122:\t\n1123:\t\tzs = zones_state[disk_zone_no(disk, sector)];\n1124:\t\tzone-\u003econd = blk_zstate_to_zone_cond(zs);\n1125:\t\tif (blk_zstate_is_conv(zs))\n1126:\t\t\tzone-\u003etype = BLK_ZONE_TYPE_CONVENTIONAL;\n1127:\t\telse\n1128:\t\t\tzone-\u003etype = BLK_ZONE_TYPE_SEQWRITE_REQ;\n1129:\t\trcu_read_unlock();\n1130:\t\n1131:\t\tzone-\u003estart = sector;\n1132:\t\tzone-\u003elen = zone_sectors;\n1133:\t\n1134:\t\t/*\n1135:\t\t * If this is a conventional zone, we do not have a zone write plug and\n1136:\t\t * can report the zone immediately.\n1137:\t\t */\n1138:\t\tif (zone-\u003etype == BLK_ZONE_TYPE_CONVENTIONAL) {\n1139:\t\t\tzone-\u003ecapacity = zone_sectors;\n1140:\t\t\tzone-\u003ewp = ULLONG_MAX;\n1141:\t\t\treturn 0;\n1142:\t\t}\n1143:\t\n1144:\t\t/*\n1145:\t\t * This is a sequential write required zone. If the zone is read-only or\n1146:\t\t * offline, only set the zone write pointer to an invalid value and\n1147:\t\t * report the zone.\n1148:\t\t */\n1149:\t\tif (disk_zone_is_last(disk, zone))\n1150:\t\t\tzone-\u003ecapacity = disk-\u003elast_zone_capacity;\n1151:\t\telse\n1152:\t\t\tzone-\u003ecapacity = disk-\u003ezone_capacity;\n1153:\t\n1154:\t\tif (disk_zone_cond_is_offline_or_readonly(zone-\u003econd)) {\n"
} |
| 67/3 | 2026/08/11 12:03 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 25421 output: 313 thoughts: 1984 Thoughts: **Analysis of Zone State Transition Patch for Potential Information Leaks**
Okay, so I've just been looking over this patch that refactors how zone conditions are handled, replacing `zones_cond` with `zones_state` and introducing the `enum blk_zstate`. My initial concern, as always, is whether this introduces a risk of information leakage through uninitialized memory reads.
Line 1110 caught my eye with the `memset(zone, 0, sizeof(*zone));`. I needed to confirm whether this was new or pre-existing. It's already there! That's good. The `zone` struct is fully zeroed before any data is written into it. This means `blkdev_copy_zone_to_user` won't leak anything.
I then did a deeper dive into how this `zones_state` is allocated and used. `disk_revalidate_zone_resources` allocates `args->zones_state` using `kzalloc`. This ensures it's zeroed on allocation, and then sets the array. That's a good start. I checked other related allocations like `disk_alloc_zone_resources` and `disk_init_revalidate_args` and `blk_revalidate_disk_zones`, which also use `kcalloc` or `= { }` to guarantee zero-initialization.
The major changes appear to be about control flow and replacing the condition checking: `blk_zstate_is_conv`. These are based on the internal zone state, which I've confirmed is always initialized before use.
The patch's core is refactoring the internal storage of zone condition and type within the `gendisk` structure. It's replacing the old `zones_cond` with `zones_state`, which combines condition and type into a single byte (`enum blk_zstate`). The new `bdev_check_zone_mgmt` function now makes additional checks for offline or read-only zones.
Given all of this, I'm confident there is no risk of uninitialized memory usage or information leaks introduced by this patch. No new user-space structures are introduced, and existing ones, like `struct blk_zone`, are correctly initialized before being copied. The internal states are properly initialized using `kzalloc` or `kcalloc`, and no data is being copied from internal buffers directly to user space without initialization. It's just refactoring.
Standard memory safety tools (KASAN, LOCKDEP, etc.) should be sufficient to catch any subtle issues (like out-of-bounds access, use-after-free, or locking bugs) that might have been introduced, but I don't see any obvious KMSAN fuzzing needs here. We're good.
|
| 68/3 | 2026/08/11 12:04 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch refactors how zone conditions are stored internally in the block layer, replacing the `zones_cond` array with a `zones_state` array that packs both zone condition and zone type into a single byte. It also adds checks to prevent issuing zone management commands or writes to offline or read-only zones.\n\nAll newly introduced or modified memory allocations for these internal states (e.g., `args-\u003ezones_state` in `disk_init_revalidate_args`) use `kzalloc`, ensuring they are fully zero-initialized. The `struct blk_zone` structure, which is eventually copied to user space via `blkdev_copy_zone_to_user`, is allocated on the stack in `blkdev_report_zones_cached` but is fully zeroed out using `memset(zone, 0, sizeof(*zone))` in `blkdev_get_zone_info` before any fields are populated. \n\nSince all relevant structures and arrays are explicitly zero-initialized, there is no risk of uninitialized memory reads or kernel-to-user info-leaks. Any potential bugs introduced by this patch (such as out-of-bounds accesses, use-after-free, or logic errors) would be effectively caught by standard KASAN, LOCKDEP, and other default bug detectors. Therefore, a dedicated KMSAN fuzzing session is not required.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch refactors how zone conditions are stored internally in the block layer, replacing the `zones_cond` array with a `zones_state` array that packs both zone condition and zone type into a single byte. It also adds checks to prevent issuing zone management commands or writes to offline or read-only zones.\n\nAll newly introduced or modified memory allocations for these internal states (e.g., `args-\u003ezones_state` in `disk_init_revalidate_args`) use `kzalloc`, ensuring they are fully zero-initialized. The `struct blk_zone` structure, which is eventually copied to user space via `blkdev_copy_zone_to_user`, is allocated on the stack in `blkdev_report_zones_cached` but is fully zeroed out using `memset(zone, 0, sizeof(*zone))` in `blkdev_get_zone_info` before any fields are populated. \n\nSince all relevant structures and arrays are explicitly zero-initialized, there is no risk of uninitialized memory reads or kernel-to-user info-leaks. Any potential bugs introduced by this patch (such as out-of-bounds accesses, use-after-free, or logic errors) would be effectively caught by standard KASAN, LOCKDEP, and other default bug detectors. Therefore, a dedicated KMSAN fuzzing session is not required.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|