AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/21 11:10 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": [
    "BLK_DEV_NVME",
    "NVME_MULTIPATH",
    "BLK_DEV_DM",
    "BLK_DEV_MD"
  ],
  "FocusSymbols": [
    "blk_set_mpath_head_limits",
    "blk_stack_mpath_limits",
    "blk_stack_limits"
  ],
  "KMSANReasoning": "The patch modifies block layer settings (`blk-settings.c`) and NVMe multipath queue limits stacking (`nvme/host/core.c`, `nvme/host/multipath.c`). It introduces new helper functions (`blk_set_mpath_head_limits`, `blk_stack_mpath_limits`) to properly stack block limits for multipath devices. The structures modified (`struct queue_limits`) are fully initialized by copying existing limits (e.g., via `queue_limits_start_update()`) before being modified and committed. No uninitialized memory is introduced, read, or leaked to user space. The changes are purely logic refactoring and fixing how block limits are stacked. Therefore, a dedicated KMSAN fuzzing session is not justified, as there are no uninitialized memory risks. Standard KASAN and other bug detectors are sufficient to catch any potential memory corruption or logic bugs.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch refactors block layer queue limits stacking, introducing new functions for multipath devices (like NVMe multipath) and modifying the generic `blk_stack_limits` used by stacking drivers like DM and MD. These are core block layer and NVMe host changes that can be reached in a virtualized environment (e.g., QEMU with NVMe emulation).",
  "WorthFuzzing": true
}

1/1 2026/08/21 11:10 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit fb9299a0c733871f1b8e539c92d7b3a24a014028\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Fri Aug 21 11:10:18 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/block/blk-settings.c b/block/blk-settings.c\nindex 8274631290dbf..8f70bab0a814a 100644\n--- a/block/blk-settings.c\n+++ b/block/blk-settings.c\n@@ -653,6 +653,14 @@ static unsigned int blk_round_down_sectors(unsigned int sectors, unsigned int lb\n \treturn sectors;\n }\n \n+static void blk_clear_atomic_write_limits(struct queue_limits *lim)\n+{\n+\tlim-\u003eatomic_write_hw_max = 0;\n+\tlim-\u003eatomic_write_hw_unit_max = 0;\n+\tlim-\u003eatomic_write_hw_unit_min = 0;\n+\tlim-\u003eatomic_write_hw_boundary = 0;\n+}\n+\n /* Check if second and later bottom devices are compliant */\n static bool blk_stack_atomic_writes_tail(struct queue_limits *t,\n \t\t\t\tstruct queue_limits *b)\n@@ -726,8 +734,8 @@ static bool blk_stack_atomic_writes_head(struct queue_limits *t,\n \treturn true;\n }\n \n-static void blk_stack_atomic_writes_limits(struct queue_limits *t,\n-\t\t\t\tstruct queue_limits *b, sector_t start)\n+static bool blk_stack_atomic_writes_hw_limits(struct queue_limits *t,\n+\t\t\t\tstruct queue_limits *b)\n {\n \tif (!(b-\u003efeatures \u0026 BLK_FEAT_ATOMIC_WRITES))\n \t\tgoto unsupported;\n@@ -735,9 +743,6 @@ static void blk_stack_atomic_writes_limits(struct queue_limits *t,\n \tif (!b-\u003eatomic_write_hw_unit_min)\n \t\tgoto unsupported;\n \n-\tif (!blk_atomic_write_start_sect_aligned(start, b))\n-\t\tgoto unsupported;\n-\n \t/* UINT_MAX indicates no stacking of bottom devices yet */\n \tif (t-\u003eatomic_write_hw_max == UINT_MAX) {\n \t\tif (!blk_stack_atomic_writes_head(t, b))\n@@ -747,49 +752,32 @@ static void blk_stack_atomic_writes_limits(struct queue_limits *t,\n \t\t\tgoto unsupported;\n \t}\n \tblk_stack_atomic_writes_chunk_sectors(t);\n-\treturn;\n+\treturn true;\n \n unsupported:\n-\tt-\u003eatomic_write_hw_max = 0;\n-\tt-\u003eatomic_write_hw_unit_max = 0;\n-\tt-\u003eatomic_write_hw_unit_min = 0;\n-\tt-\u003eatomic_write_hw_boundary = 0;\n+\tblk_clear_atomic_write_limits(t);\n+\treturn false;\n }\n \n-/**\n- * blk_stack_limits - adjust queue_limits for stacked devices\n- * @t:\tthe stacking driver limits (top device)\n- * @b:  the underlying queue limits (bottom, component device)\n- * @start:  first data sector within component device\n- *\n- * Description:\n- *    This function is used by stacking drivers like MD and DM to ensure\n- *    that all component devices have compatible block sizes and\n- *    alignments.  The stacking driver must provide a queue_limits\n- *    struct (top) and then iteratively call the stacking function for\n- *    all component (bottom) devices.  The stacking function will\n- *    attempt to combine the values and ensure proper alignment.\n- *\n- *    Returns 0 if the top and bottom queue_limits are compatible.  The\n- *    top device's block sizes and alignment offsets may be adjusted to\n- *    ensure alignment with the bottom device. If no compatible sizes\n- *    and alignments exist, -1 is returned and the resulting top\n- *    queue_limits will have the misaligned flag set to indicate that\n- *    the alignment_offset is undefined.\n- */\n-int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,\n-\t\t     sector_t start)\n+static void blk_stack_atomic_writes_limits(struct queue_limits *t,\n+\t\t\t\tstruct queue_limits *b, sector_t start)\n {\n-\tunsigned int top, bottom, alignment;\n-\tint ret = 0;\n+\tif (blk_stack_atomic_writes_hw_limits(t, b) \u0026\u0026\n+\t    !blk_atomic_write_start_sect_aligned(start, b))\n+\t\tblk_clear_atomic_write_limits(t);\n+}\n \n-\tt-\u003efeatures |= (b-\u003efeatures \u0026 BLK_FEAT_INHERIT_MASK);\n+#define STACK_MIN_NOT_ZERO(t, b, field) \\\n+\t((t)-\u003efield = min_not_zero((t)-\u003efield, (b)-\u003efield))\n+#define STACK_MIN(t, b, field) \\\n+\t((t)-\u003efield = min((t)-\u003efield, (b)-\u003efield))\n \n+static void blk_stack_path_limits(struct queue_limits *t,\n+\t\tconst struct queue_limits *b)\n+{\n \t/*\n-\t * Some feaures need to be supported both by the stacking driver and all\n-\t * underlying devices.  The stacking driver sets these flags before\n-\t * stacking the limits, and this will clear the flags if any of the\n-\t * underlying devices does not support it.\n+\t * These features must be supported by the top queue and every path that\n+\t * can execute I/O. Clear them when a path does not support them.\n \t */\n \tif (!(b-\u003efeatures \u0026 BLK_FEAT_NOWAIT))\n \t\tt-\u003efeatures \u0026= ~BLK_FEAT_NOWAIT;\n@@ -798,51 +786,102 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,\n \tif (!(b-\u003efeatures \u0026 BLK_FEAT_PCI_P2PDMA))\n \t\tt-\u003efeatures \u0026= ~BLK_FEAT_PCI_P2PDMA;\n \n-\tt-\u003eflags |= (b-\u003eflags \u0026 BLK_FLAG_MISALIGNED);\n+\tSTACK_MIN_NOT_ZERO(t, b, max_hw_sectors);\n+\tSTACK_MIN_NOT_ZERO(t, b, max_dev_sectors);\n+\tSTACK_MIN_NOT_ZERO(t, b, seg_boundary_mask);\n+\tSTACK_MIN_NOT_ZERO(t, b, virt_boundary_mask);\n+\tSTACK_MIN_NOT_ZERO(t, b, max_segments);\n+\tSTACK_MIN_NOT_ZERO(t, b, max_integrity_segments);\n+\tSTACK_MIN_NOT_ZERO(t, b, max_segment_size);\n+\tSTACK_MIN(t, b, max_write_zeroes_sectors);\n+\tSTACK_MIN(t, b, max_hw_wzeroes_unmap_sectors);\n+\tSTACK_MIN_NOT_ZERO(t, b, max_discard_segments);\n+\tSTACK_MIN(t, b, max_hw_zone_append_sectors);\n+\tt-\u003edma_alignment = max(t-\u003edma_alignment, b-\u003edma_alignment);\n+}\n \n-\tt-\u003emax_sectors = min_not_zero(t-\u003emax_sectors, b-\u003emax_sectors);\n-\tt-\u003emax_user_sectors = min_not_zero(t-\u003emax_user_sectors,\n-\t\t\tb-\u003emax_user_sectors);\n-\tt-\u003emax_hw_sectors = min_not_zero(t-\u003emax_hw_sectors, b-\u003emax_hw_sectors);\n-\tt-\u003emax_dev_sectors = min_not_zero(t-\u003emax_dev_sectors, b-\u003emax_dev_sectors);\n-\tt-\u003emax_write_zeroes_sectors = min(t-\u003emax_write_zeroes_sectors,\n-\t\t\t\t\tb-\u003emax_write_zeroes_sectors);\n-\tt-\u003emax_user_wzeroes_unmap_sectors =\n-\t\t\tmin(t-\u003emax_user_wzeroes_unmap_sectors,\n-\t\t\t    b-\u003emax_user_wzeroes_unmap_sectors);\n-\tt-\u003emax_hw_wzeroes_unmap_sectors =\n-\t\t\tmin(t-\u003emax_hw_wzeroes_unmap_sectors,\n-\t\t\t    b-\u003emax_hw_wzeroes_unmap_sectors);\n+/**\n+ * blk_set_mpath_head_limits - set head limits common to all paths\n+ * @t: limits for the multipath head\n+ * @b: limits for one path\n+ *\n+ * Set head limits that are expected to be identical across paths. Stack\n+ * limits that may differ between paths with blk_stack_mpath_limits().\n+ */\n+void blk_set_mpath_head_limits(struct queue_limits *t,\n+\t\t\t       struct queue_limits *b)\n+{\n+\tt-\u003elogical_block_size = b-\u003elogical_block_size;\n+\tt-\u003ephysical_block_size = b-\u003ephysical_block_size;\n+\tt-\u003ealignment_offset = b-\u003ealignment_offset;\n+\tt-\u003eio_min = b-\u003eio_min;\n+\tt-\u003eio_opt = b-\u003eio_opt;\n+\tt-\u003ediscard_granularity = b-\u003ediscard_granularity;\n+\tt-\u003ediscard_alignment = b-\u003ediscard_alignment;\n+\tt-\u003ezone_write_granularity = b-\u003ezone_write_granularity;\n+\tt-\u003emax_write_streams = b-\u003emax_write_streams;\n+\tt-\u003ewrite_stream_granularity = b-\u003ewrite_stream_granularity;\n+}\n+EXPORT_SYMBOL_GPL(blk_set_mpath_head_limits);\n \n-\tt-\u003emax_hw_zone_append_sectors = min(t-\u003emax_hw_zone_append_sectors,\n-\t\t\t\t\tb-\u003emax_hw_zone_append_sectors);\n+/**\n+ * blk_stack_mpath_limits - stack limits across same-LBA multipath paths\n+ * @t: limits for the multipath head\n+ * @b: limits for one path\n+ *\n+ * Stack limits in @b that may differ between paths. Unlike\n+ * blk_stack_limits(), this does not apply mapped-range topology or a mapping\n+ * offset. Set limits that are expected to be identical across paths with\n+ * blk_set_mpath_head_limits().\n+ *\n+ * Initialize @t with blk_set_stacking_limits() and set features that require\n+ * support from every path before the first call. Set\n+ * @t-\u003emax_hw_discard_sectors to UINT_MAX and call once for each path. A zero\n+ * discard limit disables discard for the head.\n+ */\n+void blk_stack_mpath_limits(struct queue_limits *t, struct queue_limits *b)\n+{\n+\tif (b-\u003echunk_sectors)\n+\t\tt-\u003echunk_sectors = gcd(t-\u003echunk_sectors, b-\u003echunk_sectors);\n \n-\tt-\u003eseg_boundary_mask = min_not_zero(t-\u003eseg_boundary_mask,\n-\t\t\t\t\t    b-\u003eseg_boundary_mask);\n-\tt-\u003evirt_boundary_mask = min_not_zero(t-\u003evirt_boundary_mask,\n-\t\t\t\t\t    b-\u003evirt_boundary_mask);\n+\tt-\u003efeatures |= b-\u003efeatures \u0026\n+\t\t(BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA |\n+\t\t BLK_FEAT_ROTATIONAL | BLK_FEAT_STABLE_WRITES);\n+\tblk_stack_path_limits(t, b);\n+\tSTACK_MIN(t, b, max_hw_discard_sectors);\n+\tblk_stack_atomic_writes_hw_limits(t, b);\n+\n+\tif (t-\u003efeatures \u0026 BLK_FEAT_ZONED) {\n+\t\tSTACK_MIN_NOT_ZERO(t, b, max_open_zones);\n+\t\tSTACK_MIN_NOT_ZERO(t, b, max_active_zones);\n+\t}\n+}\n+EXPORT_SYMBOL_GPL(blk_stack_mpath_limits);\n \n-\tt-\u003emax_segments = min_not_zero(t-\u003emax_segments, b-\u003emax_segments);\n-\tt-\u003emax_discard_segments = min_not_zero(t-\u003emax_discard_segments,\n-\t\t\t\t\t       b-\u003emax_discard_segments);\n-\tt-\u003emax_integrity_segments = min_not_zero(t-\u003emax_integrity_segments,\n-\t\t\t\t\t\t b-\u003emax_integrity_segments);\n+/*\n+ * Stack block sizes, I/O granularities, chunk boundaries and alignment for a\n+ * bottom-device range mapped at @start. Round maximum sector limits after the\n+ * resulting logical block size is known.\n+ */\n+static int blk_stack_topology_limits(struct queue_limits *t,\n+\t\tconst struct queue_limits *b, sector_t start)\n+{\n+\tunsigned int top, bottom, alignment;\n+\tint ret = 0;\n \n-\tt-\u003emax_segment_size = min_not_zero(t-\u003emax_segment_size,\n-\t\t\t\t\t   b-\u003emax_segment_size);\n+\tt-\u003eflags |= b-\u003eflags \u0026 BLK_FLAG_MISALIGNED;\n \n \talignment = queue_limit_alignment_offset(b, start);\n \n-\t/* Bottom device has different alignment.  Check that it is\n+\t/*\n+\t * The bottom device has a different alignment. Check that it is\n \t * compatible with the current top alignment.\n \t */\n \tif (t-\u003ealignment_offset != alignment) {\n-\n-\t\ttop = max(t-\u003ephysical_block_size, t-\u003eio_min)\n-\t\t\t+ t-\u003ealignment_offset;\n+\t\ttop = max(t-\u003ephysical_block_size, t-\u003eio_min) + t-\u003ealignment_offset;\n \t\tbottom = max(b-\u003ephysical_block_size, b-\u003eio_min) + alignment;\n \n-\t\t/* Verify that top and bottom intervals line up */\n+\t\t/* Verify that top and bottom intervals line up. */\n \t\tif (max(top, bottom) % min(top, bottom)) {\n \t\t\tt-\u003eflags |= BLK_FLAG_MISALIGNED;\n \t\t\tret = -1;\n@@ -851,15 +890,12 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,\n \n \tt-\u003elogical_block_size = max(t-\u003elogical_block_size,\n \t\t\t\t    b-\u003elogical_block_size);\n-\n \tt-\u003ephysical_block_size = max(t-\u003ephysical_block_size,\n \t\t\t\t     b-\u003ephysical_block_size);\n-\n \tt-\u003eio_min = max(t-\u003eio_min, b-\u003eio_min);\n \tt-\u003eio_opt = lcm_not_zero(t-\u003eio_opt, b-\u003eio_opt);\n-\tt-\u003edma_alignment = max(t-\u003edma_alignment, b-\u003edma_alignment);\n \n-\t/* Set non-power-of-2 compatible chunk_sectors boundary */\n+\t/* Set non-power-of-2 compatible chunk_sectors boundary. */\n \tif (b-\u003echunk_sectors)\n \t\tt-\u003echunk_sectors = gcd(t-\u003echunk_sectors, b-\u003echunk_sectors);\n \n@@ -891,19 +927,64 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,\n \t\tret = -1;\n \t}\n \n-\t/* Find lowest common alignment_offset */\n-\tt-\u003ealignment_offset = lcm_not_zero(t-\u003ealignment_offset, alignment)\n-\t\t% max(t-\u003ephysical_block_size, t-\u003eio_min);\n+\t/* Find lowest common alignment_offset. */\n+\tt-\u003ealignment_offset = lcm_not_zero(t-\u003ealignment_offset, alignment) %\n+\t\tmax(t-\u003ephysical_block_size, t-\u003eio_min);\n \n-\t/* Verify that new alignment_offset is on a logical block boundary */\n+\t/* Verify that new alignment_offset is on a logical block boundary. */\n \tif (t-\u003ealignment_offset \u0026 (t-\u003elogical_block_size - 1)) {\n \t\tt-\u003eflags |= BLK_FLAG_MISALIGNED;\n \t\tret = -1;\n \t}\n \n-\tt-\u003emax_sectors = blk_round_down_sectors(t-\u003emax_sectors, t-\u003elogical_block_size);\n-\tt-\u003emax_hw_sectors = blk_round_down_sectors(t-\u003emax_hw_sectors, t-\u003elogical_block_size);\n-\tt-\u003emax_dev_sectors = blk_round_down_sectors(t-\u003emax_dev_sectors, t-\u003elogical_block_size);\n+\tt-\u003emax_sectors = blk_round_down_sectors(t-\u003emax_sectors,\n+\t\t\t\t\t\tt-\u003elogical_block_size);\n+\tt-\u003emax_hw_sectors = blk_round_down_sectors(t-\u003emax_hw_sectors,\n+\t\t\t\t\t\t   t-\u003elogical_block_size);\n+\tt-\u003emax_dev_sectors = blk_round_down_sectors(t-\u003emax_dev_sectors,\n+\t\t\t\t\t\t    t-\u003elogical_block_size);\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * blk_stack_limits - adjust queue_limits for stacked devices\n+ * @t:\tthe stacking driver limits (top device)\n+ * @b:  the underlying queue limits (bottom, component device)\n+ * @start:  first data sector within component device\n+ *\n+ * Description:\n+ *    This function is used by stacking drivers like MD and DM to ensure\n+ *    that all component devices have compatible block sizes and\n+ *    alignments.  The stacking driver must provide a queue_limits\n+ *    struct (top) and then iteratively call the stacking function for\n+ *    all component (bottom) devices.  The stacking function will\n+ *    attempt to combine the values and ensure proper alignment.\n+ *\n+ *    Returns 0 if the top and bottom queue_limits are compatible.  The\n+ *    top device's block sizes and alignment offsets may be adjusted to\n+ *    ensure alignment with the bottom device. If no compatible sizes\n+ *    and alignments exist, -1 is returned and the resulting top\n+ *    queue_limits will have the misaligned flag set to indicate that\n+ *    the alignment_offset is undefined.\n+ */\n+int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,\n+\t\t     sector_t start)\n+{\n+\tunsigned int alignment;\n+\tint ret;\n+\n+\tt-\u003efeatures |= (b-\u003efeatures \u0026 BLK_FEAT_INHERIT_MASK);\n+\tblk_stack_path_limits(t, b);\n+\n+\tt-\u003emax_sectors = min_not_zero(t-\u003emax_sectors, b-\u003emax_sectors);\n+\tt-\u003emax_user_sectors = min_not_zero(t-\u003emax_user_sectors,\n+\t\t\tb-\u003emax_user_sectors);\n+\tt-\u003emax_user_wzeroes_unmap_sectors =\n+\t\t\tmin(t-\u003emax_user_wzeroes_unmap_sectors,\n+\t\t\t    b-\u003emax_user_wzeroes_unmap_sectors);\n+\n+\tret = blk_stack_topology_limits(t, b, start);\n \n \t/* Discard alignment and granularity */\n \tif (b-\u003ediscard_granularity) {\n@@ -911,8 +992,9 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,\n \n \t\tt-\u003emax_discard_sectors = min_not_zero(t-\u003emax_discard_sectors,\n \t\t\t\t\t\t      b-\u003emax_discard_sectors);\n-\t\tt-\u003emax_hw_discard_sectors = min_not_zero(t-\u003emax_hw_discard_sectors,\n-\t\t\t\t\t\t\t b-\u003emax_hw_discard_sectors);\n+\t\tt-\u003emax_hw_discard_sectors =\n+\t\t\tmin_not_zero(t-\u003emax_hw_discard_sectors,\n+\t\t\t\t     b-\u003emax_hw_discard_sectors);\n \t\tt-\u003ediscard_granularity = max(t-\u003ediscard_granularity,\n \t\t\t\t\t     b-\u003ediscard_granularity);\n \t\tt-\u003ediscard_alignment = lcm_not_zero(t-\u003ediscard_alignment, alignment) %\ndiff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c\nindex 1322c678f4eb8..a16986ec1c8ef 100644\n--- a/drivers/nvme/host/core.c\n+++ b/drivers/nvme/host/core.c\n@@ -2530,12 +2530,35 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,\n \treturn ret;\n }\n \n-static void nvme_stack_zone_resources(struct queue_limits *t,\n-\t\t\t\t      const struct queue_limits *b)\n+static int nvme_update_ns_head_limits(struct nvme_ns *ns,\n+\t\tstruct nvme_ns_info *info, bool unsupported)\n {\n-\tt-\u003emax_open_zones = min_not_zero(t-\u003emax_open_zones, b-\u003emax_open_zones);\n-\tt-\u003emax_active_zones =\n-\t\tmin_not_zero(t-\u003emax_active_zones, b-\u003emax_active_zones);\n+\tstruct queue_limits *ns_lim = \u0026ns-\u003edisk-\u003equeue-\u003elimits;\n+\tstruct request_queue *head_q = ns-\u003ehead-\u003edisk-\u003equeue;\n+\tstruct queue_limits lim;\n+\tunsigned int memflags;\n+\tint ret;\n+\n+\tlim = queue_limits_start_update(head_q);\n+\tmemflags = blk_mq_freeze_queue(head_q);\n+\tblk_set_mpath_head_limits(\u0026lim, ns_lim);\n+\tblk_stack_mpath_limits(\u0026lim, ns_lim);\n+\tif (unsupported)\n+\t\tns-\u003ehead-\u003edisk-\u003eflags |= GENHD_FL_HIDDEN;\n+\telse\n+\t\tnvme_init_integrity(ns-\u003ehead, \u0026lim, info);\n+\tret = queue_limits_commit_update(head_q, \u0026lim);\n+\tif (ret)\n+\t\tgoto unfreeze_head_queue;\n+\n+\tset_capacity_and_notify(ns-\u003ehead-\u003edisk, get_capacity(ns-\u003edisk));\n+\tset_disk_ro(ns-\u003ehead-\u003edisk, nvme_ns_is_readonly(ns, info));\n+\tnvme_mpath_revalidate_paths(ns-\u003ehead);\n+\tret = nvme_mpath_revalidate_zones(ns-\u003ehead);\n+\n+unfreeze_head_queue:\n+\tblk_mq_unfreeze_queue(head_q, memflags);\n+\treturn ret;\n }\n \n static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)\n@@ -2576,54 +2599,8 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)\n \t\tret = 0;\n \t}\n \n-\tif (!ret \u0026\u0026 nvme_ns_head_multipath(ns-\u003ehead)) {\n-\t\tstruct queue_limits *ns_lim = \u0026ns-\u003edisk-\u003equeue-\u003elimits;\n-\t\tstruct queue_limits lim;\n-\t\tunsigned int memflags;\n-\n-\t\tlim = queue_limits_start_update(ns-\u003ehead-\u003edisk-\u003equeue);\n-\t\tmemflags = blk_mq_freeze_queue(ns-\u003ehead-\u003edisk-\u003equeue);\n-\t\t/*\n-\t\t * queue_limits mixes values that are the hardware limitations\n-\t\t * for bio splitting with what is the device configuration.\n-\t\t *\n-\t\t * For NVMe the device configuration can change after e.g. a\n-\t\t * Format command, and we really want to pick up the new format\n-\t\t * value here.  But we must still stack the queue limits to the\n-\t\t * least common denominator for multipathing to split the bios\n-\t\t * properly.\n-\t\t *\n-\t\t * To work around this, we explicitly set the device\n-\t\t * configuration to those that we just queried, but only stack\n-\t\t * the splitting limits in to make sure we still obey possibly\n-\t\t * lower limitations of other controllers.\n-\t\t */\n-\t\tlim.logical_block_size = ns_lim-\u003elogical_block_size;\n-\t\tlim.physical_block_size = ns_lim-\u003ephysical_block_size;\n-\t\tlim.io_min = ns_lim-\u003eio_min;\n-\t\tlim.io_opt = ns_lim-\u003eio_opt;\n-\t\tqueue_limits_stack_bdev(\u0026lim, ns-\u003edisk-\u003epart0, 0,\n-\t\t\t\t\tns-\u003ehead-\u003edisk-\u003edisk_name);\n-\t\tif (lim.features \u0026 BLK_FEAT_ZONED)\n-\t\t\tnvme_stack_zone_resources(\u0026lim, ns_lim);\n-\t\tif (unsupported)\n-\t\t\tns-\u003ehead-\u003edisk-\u003eflags |= GENHD_FL_HIDDEN;\n-\t\telse\n-\t\t\tnvme_init_integrity(ns-\u003ehead, \u0026lim, info);\n-\t\tlim.max_write_streams = ns_lim-\u003emax_write_streams;\n-\t\tlim.write_stream_granularity = ns_lim-\u003ewrite_stream_granularity;\n-\t\tret = queue_limits_commit_update(ns-\u003ehead-\u003edisk-\u003equeue, \u0026lim);\n-\t\tif (ret)\n-\t\t\tgoto unfreeze_head_queue;\n-\n-\t\tset_capacity_and_notify(ns-\u003ehead-\u003edisk, get_capacity(ns-\u003edisk));\n-\t\tset_disk_ro(ns-\u003ehead-\u003edisk, nvme_ns_is_readonly(ns, info));\n-\t\tnvme_mpath_revalidate_paths(ns-\u003ehead);\n-\t\tret = nvme_mpath_revalidate_zones(ns-\u003ehead);\n-\n-unfreeze_head_queue:\n-\t\tblk_mq_unfreeze_queue(ns-\u003ehead-\u003edisk-\u003equeue, memflags);\n-\t}\n+\tif (!ret \u0026\u0026 nvme_ns_head_multipath(ns-\u003ehead))\n+\t\tret = nvme_update_ns_head_limits(ns, info, unsupported);\n \n \treturn ret;\n }\ndiff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c\nindex 75dbb58286a32..448c7f33c6874 100644\n--- a/drivers/nvme/host/multipath.c\n+++ b/drivers/nvme/host/multipath.c\n@@ -760,6 +760,8 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)\n \t\treturn 0;\n \n \tblk_set_stacking_limits(\u0026lim);\n+\t/* No path discard limit has been stacked yet. */\n+\tlim.max_hw_discard_sectors = UINT_MAX;\n \tlim.dma_alignment = 3;\n \tlim.features |= BLK_FEAT_IO_STAT | BLK_FEAT_NOWAIT |\n \t\tBLK_FEAT_POLL | BLK_FEAT_ATOMIC_WRITES | BLK_FEAT_PCI_P2PDMA;\ndiff --git a/include/linux/blkdev.h b/include/linux/blkdev.h\nindex 9213a5716f95a..2fe711c196c9e 100644\n--- a/include/linux/blkdev.h\n+++ b/include/linux/blkdev.h\n@@ -1153,6 +1153,9 @@ static inline void blk_queue_disable_write_zeroes(struct request_queue *q)\n  */\n extern void blk_set_queue_depth(struct request_queue *q, unsigned int depth);\n extern void blk_set_stacking_limits(struct queue_limits *lim);\n+void blk_set_mpath_head_limits(struct queue_limits *t,\n+\t\t\t       struct queue_limits *b);\n+void blk_stack_mpath_limits(struct queue_limits *t, struct queue_limits *b);\n extern int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,\n \t\t\t    sector_t offset);\n void queue_limits_stack_bdev(struct queue_limits *t, struct block_device *bdev,\n"
}

2/1 2026/08/21 11:10 agent patch-evaluator
1m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "BLK_DEV_NVME",
    "NVME_MULTIPATH",
    "BLK_DEV_DM",
    "BLK_DEV_MD"
  ],
  "FocusSymbols": [
    "blk_set_mpath_head_limits",
    "blk_stack_mpath_limits",
    "blk_stack_limits"
  ],
  "Reasoning": "The patch refactors block layer queue limits stacking, introducing new functions for multipath devices (like NVMe multipath) and modifying the generic `blk_stack_limits` used by stacking drivers like DM and MD. These are core block layer and NVMe host changes that can be reached in a virtualized environment (e.g., QEMU with NVMe emulation).",
  "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 fb9299a0c733871f1b8e539c92d7b3a24a014028
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Aug 21 11:10:18 2026 +0000

    syz-cluster: applied patch under review

diff --git a/block/blk-settings.c b/block/blk-settings.c
index 8274631290dbf..8f70bab0a814a 100644
--- a/block/blk-settings.c
+++ b/block/blk-settings.c
@@ -653,6 +653,14 @@ static unsigned int blk_round_down_sectors(unsigned int sectors, unsigned int lb
 	return sectors;
 }
 
+static void blk_clear_atomic_write_limits(struct queue_limits *lim)
+{
+	lim->atomic_write_hw_max = 0;
+	lim->atomic_write_hw_unit_max = 0;
+	lim->atomic_write_hw_unit_min = 0;
+	lim->atomic_write_hw_boundary = 0;
+}
+
 /* Check if second and later bottom devices are compliant */
 static bool blk_stack_atomic_writes_tail(struct queue_limits *t,
 				struct queue_limits *b)
@@ -726,8 +734,8 @@ static bool blk_stack_atomic_writes_head(struct queue_limits *t,
 	return true;
 }
 
-static void blk_stack_atomic_writes_limits(struct queue_limits *t,
-				struct queue_limits *b, sector_t start)
+static bool blk_stack_atomic_writes_hw_limits(struct queue_limits *t,
+				struct queue_limits *b)
 {
 	if (!(b->features & BLK_FEAT_ATOMIC_WRITES))
 		goto unsupported;
@@ -735,9 +743,6 @@ static void blk_stack_atomic_writes_limits(struct queue_limits *t,
 	if (!b->atomic_write_hw_unit_min)
 		goto unsupported;
 
-	if (!blk_atomic_write_start_sect_aligned(start, b))
-		goto unsupported;
-
 	/* UINT_MAX indicates no stacking of bottom devices yet */
 	if (t->atomic_write_hw_max == UINT_MAX) {
 		if (!blk_stack_atomic_writes_head(t, b))
@@ -747,49 +752,32 @@ static void blk_stack_atomic_writes_limits(struct queue_limits *t,
 			goto unsupported;
 	}
 	blk_stack_atomic_writes_chunk_sectors(t);
-	return;
+	return true;
 
 unsupported:
-	t->atomic_write_hw_max = 0;
-	t->atomic_write_hw_unit_max = 0;
-	t->atomic_write_hw_unit_min = 0;
-	t->atomic_write_hw_boundary = 0;
+	blk_clear_atomic_write_limits(t);
+	return false;
 }
 
-/**
- * blk_stack_limits - adjust queue_limits for stacked devices
- * @t:	the stacking driver limits (top device)
- * @b:  the underlying queue limits (bottom, component device)
- * @start:  first data sector within component device
- *
- * Description:
- *    This function is used by stacking drivers like MD and DM to ensure
- *    that all component devices have compatible block sizes and
- *    alignments.  The stacking driver must provide a queue_limits
- *    struct (top) and then iteratively call the stacking function for
- *    all component (bottom) devices.  The stacking function will
- *    attempt to combine the values and ensure proper alignment.
- *
- *    Returns 0 if the top and bottom queue_limits are compatible.  The
- *    top device's block sizes and alignment offsets may be adjusted to
- *    ensure alignment with the bottom device. If no compatible sizes
- *    and alignments exist, -1 is returned and the resulting top
- *    queue_limits will have the misaligned flag set to indicate that
- *    the alignment_offset is undefined.
- */
-int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
-		     sector_t start)
+static void blk_stack_atomic_writes_limits(struct queue_limits *t,
+				struct queue_limits *b, sector_t start)
 {
-	unsigned int top, bottom, alignment;
-	int ret = 0;
+	if (blk_stack_atomic_writes_hw_limits(t, b) &&
+	    !blk_atomic_write_start_sect_aligned(start, b))
+		blk_clear_atomic_write_limits(t);
+}
 
-	t->features |= (b->features & BLK_FEAT_INHERIT_MASK);
+#define STACK_MIN_NOT_ZERO(t, b, field) \
+	((t)->field = min_not_zero((t)->field, (b)->field))
+#define STACK_MIN(t, b, field) \
+	((t)->field = min((t)->field, (b)->field))
 
+static void blk_stack_path_limits(struct queue_limits *t,
+		const struct queue_limits *b)
+{
 	/*
-	 * Some feaures need to be supported both by the stacking driver and all
-	 * underlying devices.  The stacking driver sets these flags before
-	 * stacking the limits, and this will clear the flags if any of the
-	 * underlying devices does not support it.
+	 * These features must be supported by the top queue and every path that
+	 * can execute I/O. Clear them when a path does not support them.
 	 */
 	if (!(b->features & BLK_FEAT_NOWAIT))
 		t->features &= ~BLK_FEAT_NOWAIT;
@@ -798,51 +786,102 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
 	if (!(b->features & BLK_FEAT_PCI_P2PDMA))
 		t->features &= ~BLK_FEAT_PCI_P2PDMA;
 
-	t->flags |= (b->flags & BLK_FLAG_MISALIGNED);
+	STACK_MIN_NOT_ZERO(t, b, max_hw_sectors);
+	STACK_MIN_NOT_ZERO(t, b, max_dev_sectors);
+	STACK_MIN_NOT_ZERO(t, b, seg_boundary_mask);
+	STACK_MIN_NOT_ZERO(t, b, virt_boundary_mask);
+	STACK_MIN_NOT_ZERO(t, b, max_segments);
+	STACK_MIN_NOT_ZERO(t, b, max_integrity_segments);
+	STACK_MIN_NOT_ZERO(t, b, max_segment_size);
+	STACK_MIN(t, b, max_write_zeroes_sectors);
+	STACK_MIN(t, b, max_hw_wzeroes_unmap_sectors);
+	STACK_MIN_NOT_ZERO(t, b, max_discard_segments);
+	STACK_MIN(t, b, max_hw_zone_append_sectors);
+	t->dma_alignment = max(t->dma_alignment, b->dma_alignment);
+}
 
-	t->max_sectors = min_not_zero(t->max_sectors, b->max_sectors);
-	t->max_user_sectors = min_not_zero(t->max_user_sectors,
-			b->max_user_sectors);
-	t->max_hw_sectors = min_not_zero(t->max_hw_sectors, b->max_hw_sectors);
-	t->max_dev_sectors = min_not_zero(t->max_dev_sectors, b->max_dev_sectors);
-	t->max_write_zeroes_sectors = min(t->max_write_zeroes_sectors,
-					b->max_write_zeroes_sectors);
-	t->max_user_wzeroes_unmap_sectors =
-			min(t->max_user_wzeroes_unmap_sectors,
-			    b->max_user_wzeroes_unmap_sectors);
-	t->max_hw_wzeroes_unmap_sectors =
-			min(t->max_hw_wzeroes_unmap_sectors,
-			    b->max_hw_wzeroes_unmap_sectors);
+/**
+ * blk_set_mpath_head_limits - set head limits common to all paths
+ * @t: limits for the multipath head
+ * @b: limits for one path
+ *
+ * Set head limits that are expected to be identical across paths. Stack
+ * limits that may differ between paths with blk_stack_mpath_limits().
+ */
+void blk_set_mpath_head_limits(struct queue_limits *t,
+			       struct queue_limits *b)
+{
+	t->logical_block_size = b->logical_block_size;
+	t->physical_block_size = b->physical_block_size;
+	t->alignment_offset = b->alignment_offset;
+	t->io_min = b->io_min;
+	t->io_opt = b->io_opt;
+	t->discard_granularity = b->discard_granularity;
+	t->discard_alignment = b->discard_alignment;
+	t->zone_write_granularity = b->zone_write_granularity;
+	t->max_write_streams = b->max_write_streams;
+	t->write_stream_granularity = b->write_stream_granularity;
+}
+EXPORT_SYMBOL_GPL(blk_set_mpath_head_limits);
 
-	t->max_hw_zone_append_sectors = min(t->max_hw_zone_append_sectors,
-					b->max_hw_zone_append_sectors);
+/**
+ * blk_stack_mpath_limits - stack limits across same-LBA multipath paths
+ * @t: limits for the multipath head
+ * @b: limits for one path
+ *
+ * Stack limits in @b that may differ between paths. Unlike
+ * blk_stack_limits(), this does not apply mapped-range topology or a mapping
+ * offset. Set limits that are expected to be identical across paths with
+ * blk_set_mpath_head_limits().
+ *
+ * Initialize @t with blk_set_stacking_limits() and set features that require
+ * support from every path before the first call. Set
+ * @t->max_hw_discard_sectors to UINT_MAX and call once for each path. A zero
+ * discard limit disables discard for the head.
+ */
+void blk_stack_mpath_limits(struct queue_limits *t, struct queue_limits *b)
+{
+	if (b->chunk_sectors)
+		t->chunk_sectors = gcd(t->chunk_sectors, b->chunk_sectors);
 
-	t->seg_boundary_mask = min_not_zero(t->seg_boundary_mask,
-					    b->seg_boundary_mask);
-	t->virt_boundary_mask = min_not_zero(t->virt_boundary_mask,
-					    b->virt_boundary_mask);
+	t->features |= b->features &
+		(BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA |
+		 BLK_FEAT_ROTATIONAL | BLK_FEAT_STABLE_WRITES);
+	blk_stack_path_limits(t, b);
+	STACK_MIN(t, b, max_hw_discard_sectors);
+	blk_stack_atomic_writes_hw_limits(t, b);
+
+	if (t->features & BLK_FEAT_ZONED) {
+		STACK_MIN_NOT_ZERO(t, b, max_open_zones);
+		STACK_MIN_NOT_ZERO(t, b, max_active_zones);
+	}
+}
+EXPORT_SYMBOL_GPL(blk_stack_mpath_limits);
 
-	t->max_segments = min_not_zero(t->max_segments, b->max_segments);
-	t->max_discard_segments = min_not_zero(t->max_discard_segments,
-					       b->max_discard_segments);
-	t->max_integrity_segments = min_not_zero(t->max_integrity_segments,
-						 b->max_integrity_segments);
+/*
+ * Stack block sizes, I/O granularities, chunk boundaries and alignment for a
+ * bottom-device range mapped at @start. Round maximum sector limits after the
+ * resulting logical block size is known.
+ */
+static int blk_stack_topology_limits(struct queue_limits *t,
+		const struct queue_limits *b, sector_t start)
+{
+	unsigned int top, bottom, alignment;
+	int ret = 0;
 
-	t->max_segment_size = min_not_zero(t->max_segment_size,
-					   b->max_segment_size);
+	t->flags |= b->flags & BLK_FLAG_MISALIGNED;
 
 	alignment = queue_limit_alignment_offset(b, start);
 
-	/* Bottom device has different alignment.  Check that it is
+	/*
+	 * The bottom device has a different alignment. Check that it is
 	 * compatible with the current top alignment.
 	 */
 	if (t->alignment_offset != alignment) {
-
-		top = max(t->physical_block_size, t->io_min)
-			+ t->alignment_offset;
+		top = max(t->physical_block_size, t->io_min) + t->alignment_offset;
 		bottom = max(b->physical_block_size, b->io_min) + alignment;
 
-		/* Verify that top and bottom intervals line up */
+		/* Verify that top and bottom intervals line up. */
 		if (max(top, bottom) % min(top, bottom)) {
 			t->flags |= BLK_FLAG_MISALIGNED;
 			ret = -1;
@@ -851,15 +890,12 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
 
 	t->logical_block_size = max(t->logical_block_size,
 				    b->logical_block_size);
-
 	t->physical_block_size = max(t->physical_block_size,
 				     b->physical_block_size);
-
 	t->io_min = max(t->io_min, b->io_min);
 	t->io_opt = lcm_not_zero(t->io_opt, b->io_opt);
-	t->dma_alignment = max(t->dma_alignment, b->dma_alignment);
 
-	/* Set non-power-of-2 compatible chunk_sectors boundary */
+	/* Set non-power-of-2 compatible chunk_sectors boundary. */
 	if (b->chunk_sectors)
 		t->chunk_sectors = gcd(t->chunk_sectors, b->chunk_sectors);
 
@@ -891,19 +927,64 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
 		ret = -1;
 	}
 
-	/* Find lowest common alignment_offset */
-	t->alignment_offset = lcm_not_zero(t->alignment_offset, alignment)
-		% max(t->physical_block_size, t->io_min);
+	/* Find lowest common alignment_offset. */
+	t->alignment_offset = lcm_not_zero(t->alignment_offset, alignment) %
+		max(t->physical_block_size, t->io_min);
 
-	/* Verify that new alignment_offset is on a logical block boundary */
+	/* Verify that new alignment_offset is on a logical block boundary. */
 	if (t->alignment_offset & (t->logical_block_size - 1)) {
 		t->flags |= BLK_FLAG_MISALIGNED;
 		ret = -1;
 	}
 
-	t->max_sectors = blk_round_down_sectors(t->max_sectors, t->logical_block_size);
-	t->max_hw_sectors = blk_round_down_sectors(t->max_hw_sectors, t->logical_block_size);
-	t->max_dev_sectors = blk_round_down_sectors(t->max_dev_sectors, t->logical_block_size);
+	t->max_sectors = blk_round_down_sectors(t->max_sectors,
+						t->logical_block_size);
+	t->max_hw_sectors = blk_round_down_sectors(t->max_hw_sectors,
+						   t->logical_block_size);
+	t->max_dev_sectors = blk_round_down_sectors(t->max_dev_sectors,
+						    t->logical_block_size);
+
+	return ret;
+}
+
+/**
+ * blk_stack_limits - adjust queue_limits for stacked devices
+ * @t:	the stacking driver limits (top device)
+ * @b:  the underlying queue limits (bottom, component device)
+ * @start:  first data sector within component device
+ *
+ * Description:
+ *    This function is used by stacking drivers like MD and DM to ensure
+ *    that all component devices have compatible block sizes and
+ *    alignments.  The stacking driver must provide a queue_limits
+ *    struct (top) and then iteratively call the stacking function for
+ *    all component (bottom) devices.  The stacking function will
+ *    attempt to combine the values and ensure proper alignment.
+ *
+ *    Returns 0 if the top and bottom queue_limits are compatible.  The
+ *    top device's block sizes and alignment offsets may be adjusted to
+ *    ensure alignment with the bottom device. If no compatible sizes
+ *    and alignments exist, -1 is returned and the resulting top
+ *    queue_limits will have the misaligned flag set to indicate that
+ *    the alignment_offset is undefined.
+ */
+int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
+		     sector_t start)
+{
+	unsigned int alignment;
+	int ret;
+
+	t->features |= (b->features & BLK_FEAT_INHERIT_MASK);
+	blk_stack_path_limits(t, b);
+
+	t->max_sectors = min_not_zero(t->max_sectors, b->max_sectors);
+	t->max_user_sectors = min_not_zero(t->max_user_sectors,
+			b->max_user_sectors);
+	t->max_user_wzeroes_unmap_sectors =
+			min(t->max_user_wzeroes_unmap_sectors,
+			    b->max_user_wzeroes_unmap_sectors);
+
+	ret = blk_stack_topology_limits(t, b, start);
 
 	/* Discard alignment and granularity */
 	if (b->discard_granularity) {
@@ -911,8 +992,9 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
 
 		t->max_discard_sectors = min_not_zero(t->max_discard_sectors,
 						      b->max_discard_sectors);
-		t->max_hw_discard_sectors = min_not_zero(t->max_hw_discard_sectors,
-							 b->max_hw_discard_sectors);
+		t->max_hw_discard_sectors =
+			min_not_zero(t->max_hw_discard_sectors,
+				     b->max_hw_discard_sectors);
 		t->discard_granularity = max(t->discard_granularity,
 					     b->discard_granularity);
 		t->discard_alignment = lcm_not_zero(t->discard_alignment, alignment) %
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 1322c678f4eb8..a16986ec1c8ef 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -2530,12 +2530,35 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
 	return ret;
 }
 
-static void nvme_stack_zone_resources(struct queue_limits *t,
-				      const struct queue_limits *b)
+static int nvme_update_ns_head_limits(struct nvme_ns *ns,
+		struct nvme_ns_info *info, bool unsupported)
 {
-	t->max_open_zones = min_not_zero(t->max_open_zones, b->max_open_zones);
-	t->max_active_zones =
-		min_not_zero(t->max_active_zones, b->max_active_zones);
+	struct queue_limits *ns_lim = &ns->disk->queue->limits;
+	struct request_queue *head_q = ns->head->disk->queue;
+	struct queue_limits lim;
+	unsigned int memflags;
+	int ret;
+
+	lim = queue_limits_start_update(head_q);
+	memflags = blk_mq_freeze_queue(head_q);
+	blk_set_mpath_head_limits(&lim, ns_lim);
+	blk_stack_mpath_limits(&lim, ns_lim);
+	if (unsupported)
+		ns->head->disk->flags |= GENHD_FL_HIDDEN;
+	else
+		nvme_init_integrity(ns->head, &lim, info);
+	ret = queue_limits_commit_update(head_q, &lim);
+	if (ret)
+		goto unfreeze_head_queue;
+
+	set_capacity_and_notify(ns->head->disk, get_capacity(ns->disk));
+	set_disk_ro(ns->head->disk, nvme_ns_is_readonly(ns, info));
+	nvme_mpath_revalidate_paths(ns->head);
+	ret = nvme_mpath_revalidate_zones(ns->head);
+
+unfreeze_head_queue:
+	blk_mq_unfreeze_queue(head_q, memflags);
+	return ret;
 }
 
 static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)
@@ -2576,54 +2599,8 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)
 		ret = 0;
 	}
 
-	if (!ret && nvme_ns_head_multipath(ns->head)) {
-		struct queue_limits *ns_lim = &ns->disk->queue->limits;
-		struct queue_limits lim;
-		unsigned int memflags;
-
-		lim = queue_limits_start_update(ns->head->disk->queue);
-		memflags = blk_mq_freeze_queue(ns->head->disk->queue);
-		/*
-		 * queue_limits mixes values that are the hardware limitations
-		 * for bio splitting with what is the device configuration.
-		 *
-		 * For NVMe the device configuration can change after e.g. a
-		 * Format command, and we really want to pick up the new format
-		 * value here.  But we must still stack the queue limits to the
-		 * least common denominator for multipathing to split the bios
-		 * properly.
-		 *
-		 * To work around this, we explicitly set the device
-		 * configuration to those that we just queried, but only stack
-		 * the splitting limits in to make sure we still obey possibly
-		 * lower limitations of other controllers.
-		 */
-		lim.logical_block_size = ns_lim->logical_block_size;
-		lim.physical_block_size = ns_lim->physical_block_size;
-		lim.io_min = ns_lim->io_min;
-		lim.io_opt = ns_lim->io_opt;
-		queue_limits_stack_bdev(&lim, ns->disk->part0, 0,
-					ns->head->disk->disk_name);
-		if (lim.features & BLK_FEAT_ZONED)
-			nvme_stack_zone_resources(&lim, ns_lim);
-		if (unsupported)
-			ns->head->disk->flags |= GENHD_FL_HIDDEN;
-		else
-			nvme_init_integrity(ns->head, &lim, info);
-		lim.max_write_streams = ns_lim->max_write_streams;
-		lim.write_stream_granularity = ns_lim->write_stream_granularity;
-		ret = queue_limits_commit_update(ns->head->disk->queue, &lim);
-		if (ret)
-			goto unfreeze_head_queue;
-
-		set_capacity_and_notify(ns->head->disk, get_capacity(ns->disk));
-		set_disk_ro(ns->head->disk, nvme_ns_is_readonly(ns, info));
-		nvme_mpath_revalidate_paths(ns->head);
-		ret = nvme_mpath_revalidate_zones(ns->head);
-
-unfreeze_head_queue:
-		blk_mq_unfreeze_queue(ns->head->disk->queue, memflags);
-	}
+	if (!ret && nvme_ns_head_multipath(ns->head))
+		ret = nvme_update_ns_head_limits(ns, info, unsupported);
 
 	return ret;
 }
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 75dbb58286a32..448c7f33c6874 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -760,6 +760,8 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
 		return 0;
 
 	blk_set_stacking_limits(&lim);
+	/* No path discard limit has been stacked yet. */
+	lim.max_hw_discard_sectors = UINT_MAX;
 	lim.dma_alignment = 3;
 	lim.features |= BLK_FEAT_IO_STAT | BLK_FEAT_NOWAIT |
 		BLK_FEAT_POLL | BLK_FEAT_ATOMIC_WRITES | BLK_FEAT_PCI_P2PDMA;
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 9213a5716f95a..2fe711c196c9e 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1153,6 +1153,9 @@ static inline void blk_queue_disable_write_zeroes(struct request_queue *q)
  */
 extern void blk_set_queue_depth(struct request_queue *q, unsigned int depth);
 extern void blk_set_stacking_limits(struct queue_limits *lim);
+void blk_set_mpath_head_limits(struct queue_limits *t,
+			       struct queue_limits *b);
+void blk_stack_mpath_limits(struct queue_limits *t, struct queue_limits *b);
 extern int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
 			    sector_t offset);
 void queue_limits_stack_bdev(struct queue_limits *t, struct block_device *bdev,

3/2 2026/08/21 11:10 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 8800
							output: 22
							thoughts: 230
						

Thoughts:
**Analyzing the Block Layer Queue Limits Patch**

Okay, so we've got this patch – interesting. It's touching the block layer's queue limit handling, specifically how those limits stack when dealing with multipath devices, and specifically, it's focused on NVMe multipath. The patch introduces `blk_set_mpath_head_limits` and `blk_stack_mpath_limits` functions within `block/blk-settings.c`, which makes sense. Then, the patch uses these functions within the NVMe driver code, specifically `drivers/nvme/host/core.c` and `drivers/nvme/host/multipath.c`.

Functionally, this seems like a solid change to both the block layer and how NVMe multipath operates, right? It's fundamentally altering the way queue limits are stacked for these multipath devices. The next logical step is figuring out whether this can actually be exercised in a practical testing scenario. Can we reach this functionality in a virtualized environment? Absolutely. We can emulate NVMe devices in QEMU, so NVMe multipath testing shouldn't be a problem. Wait, does QEMU actually support NVMe multipath? It does, multiple controllers can attach to the same namespace.

And given that the changes to the block layer seem generic, even if they're currently *being used* by NVMe, they could be relevant to other multipath implementations. Let's dig deeper. I need to check if other drivers/subsystems are using `blk_stack_mpath_limits`. I'll fire up `grepper` and see if any other code is calling it.



4/2 2026/08/21 11:10 tool grepper
0m Args:
{
  "Expression": "blk_stack_mpath_limits"
}

Results:
{
  "Output": "block/blk-settings.c=775=static void blk_stack_path_limits(struct queue_limits *t,\n--\nblock/blk-settings.c-808- * Set head limits that are expected to be identical across paths. Stack\nblock/blk-settings.c:809: * limits that may differ between paths with blk_stack_mpath_limits().\nblock/blk-settings.c-810- */\n--\nblock/blk-settings.c=825=EXPORT_SYMBOL_GPL(blk_set_mpath_head_limits);\n--\nblock/blk-settings.c-827-/**\nblock/blk-settings.c:828: * blk_stack_mpath_limits - stack limits across same-LBA multipath paths\nblock/blk-settings.c-829- * @t: limits for the multipath head\n--\nblock/blk-settings.c-841- */\nblock/blk-settings.c:842:void blk_stack_mpath_limits(struct queue_limits *t, struct queue_limits *b)\nblock/blk-settings.c-843-{\n--\nblock/blk-settings.c-858-}\nblock/blk-settings.c:859:EXPORT_SYMBOL_GPL(blk_stack_mpath_limits);\nblock/blk-settings.c-860-\n--\ndrivers/nvme/host/core.c=2533=static int nvme_update_ns_head_limits(struct nvme_ns *ns,\n--\ndrivers/nvme/host/core.c-2544-\tblk_set_mpath_head_limits(\u0026lim, ns_lim);\ndrivers/nvme/host/core.c:2545:\tblk_stack_mpath_limits(\u0026lim, ns_lim);\ndrivers/nvme/host/core.c-2546-\tif (unsupported)\n--\ninclude/linux/blkdev.h=1156=void blk_set_mpath_head_limits(struct queue_limits *t,\ninclude/linux/blkdev.h-1157-\t\t\t       struct queue_limits *b);\ninclude/linux/blkdev.h:1158:void blk_stack_mpath_limits(struct queue_limits *t, struct queue_limits *b);\ninclude/linux/blkdev.h-1159-extern int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,\n"
}

5/2 2026/08/21 11:10 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10021
							output: 20
							thoughts: 99
						

Thoughts:
**Fuzzing Potential in NVMe Namespace Updates**

Okay, so I've been looking at how namespace head limits are handled in the NVMe driver, specifically within `nvme_update_ns_head_limits`.  My initial thought is that the call to `blk_set_mpath_head_limits` and `blk_stack_mpath_limits` *within* this function is a potential point for fuzzing. This is a clear functional modification, and those are always interesting from a security perspective. We can almost certainly craft some interesting inputs to probe these limits and how they interact with multipath configurations.

Given that these functions are invoked, `BLK_DEV_NVME` seems a given, and `NVME_MULTIPATH` is likely crucial as well. I'll need to double-check the Kconfig options to be certain though, so I'll quickly check the `drivers/nvme/host/Kconfig` file to confirm that `NVME_MULTIPATH` is indeed the relevant configuration option. I expect it is, but it's always best to be sure of the build context before I start constructing fuzzing harnesses.



6/2 2026/08/21 11:10 tool grepper
0m Args:
{
  "Expression": "config NVME_MULTIPATH"
}

Results:
{
  "Output": "drivers/nvme/host/Kconfig=5=config BLK_DEV_NVME\n--\ndrivers/nvme/host/Kconfig-16-\ndrivers/nvme/host/Kconfig:17:config NVME_MULTIPATH\ndrivers/nvme/host/Kconfig-18-\tbool \"NVMe multipath support\"\n"
}

7/2 2026/08/21 11:10 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10476
							output: 36
							thoughts: 28
						

Thoughts:
Okay, let's dive into this. Here's how I'd approach that kernel configuration question, assuming I'm at least a *fairly* knowledgeable kernel developer:

**Investigating NVME_MULTIPATH Dependencies**

Alright, the task at hand is to figure out what `NVME_MULTIPATH` depends on. My first instinct, given my experience with kernel code, is to go directly to the source of truth for configuration options related to NVMe host drivers: `drivers/nvme/host/Kconfig`. That's where all the dependencies and other configuration information should be defined. I'll need to open that file and then hunt for the `NVME_MULTIPATH` entry. I expect to find a `depends on` clause, which will give me the information I need. Hopefully, it's a straightforward dependency chain, and I won't have to trace through multiple Kconfig files! If it *is* complex, I'll need to start tracing through includes and other dependencies. Let's see what we've got!



8/2 2026/08/21 11:10 tool read-file
0m Args:
{
  "File": "drivers/nvme/host/Kconfig",
  "FirstLine": 17,
  "LineCount": 10
}

Results:
{
  "Contents": "  17:\tconfig NVME_MULTIPATH\n  18:\t\tbool \"NVMe multipath support\"\n  19:\t\tdepends on NVME_CORE\n  20:\t\thelp\n  21:\t\t  This option controls support for multipath access to NVMe\n  22:\t\t  subsystems. If this option is enabled support for NVMe multipath\n  23:\t\t  access is included in the kernel. If this option is disabled support\n  24:\t\t  for NVMe multipath access is excluded from the kernel. When this\n  25:\t\t  option is disabled each controller/namespace receives its\n  26:\t\t  own /dev/nvmeXnY device entry and NVMe multipath access is\n"
}

9/2 2026/08/21 11:10 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10941
							output: 35
							thoughts: 50
						

Thoughts:
**Examining the NVMe Driver Configuration**

Okay, so I'm looking at this `NVME_MULTIPATH` configuration option. Clearly, it's dependent on `NVME_CORE`, which makes perfect sense. Multipathing wouldn't function without the core NVMe driver functionality. Now, where is `NVME_CORE` being selected? Ah, it's tied to `BLK_DEV_NVME` and potentially other options. Right, let's take a closer look at `BLK_DEV_NVME` to understand how this whole dependency chain works. I need to see how the base driver gets enabled before I can fully grasp the implications of multi-pathing support.



10/2 2026/08/21 11:10 tool read-file
0m Args:
{
  "File": "drivers/nvme/host/Kconfig",
  "FirstLine": 1,
  "LineCount": 16
}

Results:
{
  "Contents": "   1:\t# SPDX-License-Identifier: GPL-2.0-only\n   2:\tconfig NVME_CORE\n   3:\t\ttristate\n   4:\t\n   5:\tconfig BLK_DEV_NVME\n   6:\t\ttristate \"NVM Express block device\"\n   7:\t\tdepends on PCI \u0026\u0026 BLOCK\n   8:\t\tselect NVME_CORE\n   9:\t\thelp\n  10:\t\t  The NVM Express driver is for solid state drives directly\n  11:\t\t  connected to the PCI or PCI Express bus.  If you know you\n  12:\t\t  don't have one of these, it is safe to answer N.\n  13:\t\n  14:\t\t  To compile this driver as a module, choose M here: the\n  15:\t\t  module will be called nvme.\n  16:\t\n"
}

11/2 2026/08/21 11:10 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 11388
							output: 22
							thoughts: 467
						

Thoughts:
**My Analysis of NVMe Configuration and Block Layer Limits**

Okay, so I'm looking at this NVMe configuration and the related block layer limits. First things first, the core configurations are `BLK_DEV_NVME` and `NVME_MULTIPATH`. I need to figure out if there are any other relevant NVMe configs to consider. `NVME_TARGET` comes to mind, but for this testing, the host side is sufficient, and QEMU's NVMe emulation via PCI seems like the right approach, so `BLK_DEV_NVME` alone should do the trick. Also, quick note, `NVME_MULTIPATH` is just a boolean.

Now, let's nail down the focus symbols. I'm seeing `blk_set_mpath_head_limits`, `blk_stack_mpath_limits`, and `nvme_update_ns_head_limits`. Hmm, `nvme_update_ns_head_limits` is static. I have to remember that I want to favor non-static, non-inlined entrypoint functions, and this is called from `nvme_update_ns_info`, which in turn is called by `nvme_alloc_ns` and `nvme_ns_info_changed`. That makes it a helper. Therefore, `blk_set_mpath_head_limits` and `blk_stack_mpath_limits` are more desirable.

There's also `blk_stack_limits`, which is a generic block layer function and is modified by this patch. Since it's heavily used, and the patch modifies it, it seems like a reasonable candidate too. The patch also introduces static `blk_stack_path_limits` and `blk_stack_topology_limits`.

So, the exported functions that are directly relevant are: `blk_set_mpath_head_limits`, `blk_stack_mpath_limits`, and `blk_stack_limits`.

Let me just double-check that `nvme_update_ns_head_limits` is indeed static. Yup, it is. Wait a minute, is `nvme_update_ns_info` also static? I should check that...



12/2 2026/08/21 11:10 tool grepper
0m Args:
{
  "Expression": "nvme_update_ns_info"
}

Results:
{
  "Output": "drivers/nvme/host/core.c=2206=static void nvme_set_chunk_sectors(struct nvme_ns *ns, struct nvme_id_ns *id,\n--\ndrivers/nvme/host/core.c-2237-\ndrivers/nvme/host/core.c:2238:static int nvme_update_ns_info_generic(struct nvme_ns *ns,\ndrivers/nvme/host/core.c-2239-\t\tstruct nvme_ns_info *info)\n--\ndrivers/nvme/host/core.c=2403=static bool nvme_invalid_lba_sz(u64 nsze, signed int shift, sector_t *capacity)\n--\ndrivers/nvme/host/core.c-2407-\ndrivers/nvme/host/core.c:2408:static int nvme_update_ns_info_block(struct nvme_ns *ns,\ndrivers/nvme/host/core.c-2409-\t\tstruct nvme_ns_info *info)\n--\ndrivers/nvme/host/core.c=2533=static int nvme_update_ns_head_limits(struct nvme_ns *ns,\n--\ndrivers/nvme/host/core.c-2563-\ndrivers/nvme/host/core.c:2564:static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)\ndrivers/nvme/host/core.c-2565-{\n--\ndrivers/nvme/host/core.c-2574-\t\t\t\tinfo-\u003ensid);\ndrivers/nvme/host/core.c:2575:\t\t\tret = nvme_update_ns_info_generic(ns, info);\ndrivers/nvme/host/core.c-2576-\t\t\tbreak;\ndrivers/nvme/host/core.c-2577-\t\t}\ndrivers/nvme/host/core.c:2578:\t\tret = nvme_update_ns_info_block(ns, info);\ndrivers/nvme/host/core.c-2579-\t\tbreak;\ndrivers/nvme/host/core.c-2580-\tcase NVME_CSI_NVM:\ndrivers/nvme/host/core.c:2581:\t\tret = nvme_update_ns_info_block(ns, info);\ndrivers/nvme/host/core.c-2582-\t\tbreak;\n--\ndrivers/nvme/host/core.c-2586-\t\t\tinfo-\u003ensid, info-\u003eids.csi);\ndrivers/nvme/host/core.c:2587:\t\tret = nvme_update_ns_info_generic(ns, info);\ndrivers/nvme/host/core.c-2588-\t\tbreak;\n--\ndrivers/nvme/host/core.c=4206=static void nvme_alloc_ns(struct nvme_ctrl *ctrl, struct nvme_ns_info *info)\n--\ndrivers/nvme/host/core.c-4260-\ndrivers/nvme/host/core.c:4261:\tif (nvme_update_ns_info(ns, info))\ndrivers/nvme/host/core.c-4262-\t\tgoto out_unlink_ns;\n--\ndrivers/nvme/host/core.c=4384=static void nvme_validate_ns(struct nvme_ns *ns, struct nvme_ns_info *info)\n--\ndrivers/nvme/host/core.c-4393-\ndrivers/nvme/host/core.c:4394:\tret = nvme_update_ns_info(ns, info);\ndrivers/nvme/host/core.c-4395-out:\n"
}

13/2 2026/08/21 11:10 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 13311
							output: 21
							thoughts: 173
						

Thoughts:
**Analyzing Block Device Limit Configurations**

Okay, so I'm looking at these entry points: `blk_set_mpath_head_limits`, `blk_stack_mpath_limits`, and `blk_stack_limits`. The immediate concern is always performance, so I'm mentally checking if `blk_stack_limits` is a potential bottleneck, a hot-path function, given its role. Ah, right, it's used during block device initialization and device stacking, things like MD, Device Mapper, and NVMe multipathing. Good, that's not as critical as, say, something like `submit_bio`. So, including it shouldn't be a problem from a performance standpoint.

Next, I need to double-check the configuration options. I see `BLK_DEV_NVME` and `NVME_MULTIPATH` are in play. Are there any other configs I need to consider for complete coverage? I also see this patch modifies `blk_stack_atomic_writes_limits` in `block/blk-settings.c`. Hmm, atomic writes. That's a different area. I need to make sure I understand if any config options are specific to those atomic write limitations and make sure I don't overlook anything during the review.



14/2 2026/08/21 11:10 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "config.*ATOMIC_WRITES"
}

Results:
{
  "error": "no matches"
}

15/2 2026/08/21 11:10 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 13787
							output: 21
							thoughts: 44
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own:

**Investigating Atomic Write Configuration Options**

Alright, so I'm digging into how atomic writes are handled, and it seems like there isn't a dedicated, explicit configuration option for them just yet. Hmm, that's a bit unusual. Let's see what else might be relevant in this context. I need to be thorough and consider all angles.

My immediate thought is to check for related configurations, anything that might indirectly influence atomic write behavior. I'm going to scan for things that deal with block device management and the underlying mechanisms. The `MD` (Multiple Devices) or `BLK_DEV_DM` (Device Mapper) options jump out as potential leads. They both manage block devices and are likely to interact with `blk_stack_limits`. They might be key to unlocking the configuration I'm looking for. Let's see how deep we can go.



16/2 2026/08/21 11:11 tool grepper
0m Args:
{
  "Expression": "config BLK_DEV_DM"
}

Results:
{
  "Output": "drivers/md/Kconfig=219=source \"drivers/md/bcache/Kconfig\"\ndrivers/md/Kconfig-220-\ndrivers/md/Kconfig:221:config BLK_DEV_DM_BUILTIN\ndrivers/md/Kconfig-222-\tbool\ndrivers/md/Kconfig-223-\ndrivers/md/Kconfig:224:config BLK_DEV_DM\ndrivers/md/Kconfig-225-\ttristate \"Device mapper support\"\n"
}

17/2 2026/08/21 11:11 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14189
							output: 21
							thoughts: 7
						

Thoughts:
Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and delivered in the first person:

**My Quick Mental Checklist**

Alright, before diving too deep into the problem, my initial move is to quickly run through my standard mental checklist. Given the potential complexities of this situation, my first thought is to assess the current state. "Let's check `MD`". That is, I need to get a clear picture of what I am dealing with. I need to take inventory of where my team is, what tools or resources are available.

Now, lets see what is going on.



18/2 2026/08/21 11:11 tool grepper
0m Args:
{
  "Expression": "config BLK_DEV_MD"
}

Results:
{
  "Output": "drivers/md/Kconfig=13=if MD\ndrivers/md/Kconfig-14-\ndrivers/md/Kconfig:15:config BLK_DEV_MD\ndrivers/md/Kconfig-16-\ttristate \"RAID support\"\n"
}

19/2 2026/08/21 11:11 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14421
							output: 170
							thoughts: 384
						

Thoughts:
**My Fuzzing Strategy for Block Layer Queue Limits**

Alright, let's solidify this fuzzing plan. Based on this patch, I'm going to focus on the block layer queue limit stacking refactoring. This patch touches key functions related to multipath devices, Device Mapper, and MD RAID, all of which are critical for block I/O performance. Specifically, I'm thinking about fuzzing the interaction between `blk_set_mpath_head_limits`, `blk_stack_mpath_limits`, and `blk_stack_limits`. I've got a strong hunch that these will be good targets for finding interesting issues. I can envision problems arising from unexpected limit interactions when these stacking functions are employed. A virtualized environment, like QEMU with NVMe emulation and multipath active, will be crucial for the fuzzing setup. I can easily reproduce these configurations.

To make sure my fuzzing coverage is as thorough as possible, I'll definitely enable a few kernel configurations. I'm adding `BLK_DEV_NVME`, `NVME_MULTIPATH`, `BLK_DEV_DM`, and `BLK_DEV_MD` to my `EnableConfigs`. It's worth double-checking the configuration names, of course. Yes, "config NVME_MULTIPATH" is the exact name. And, of course, "config BLK_DEV_NVME", "config BLK_DEV_DM", and "config BLK_DEV_MD". Hold on a minute... Wait, `NVME_MULTIPATH` is dependent on `NVME_CORE` which is actually selected by `BLK_DEV_NVME`. Therefore, it looks like `BLK_DEV_NVME` and `NVME_MULTIPATH` together is really all I need. That's a good optimization to avoid unnecessary complexity.

Okay, I'm ready to move forward. Time to call `set-results` and execute this plan.



20/2 2026/08/21 11:11 tool set-results
0m Args:
{
  "EnableConfigs": [
    "BLK_DEV_NVME",
    "NVME_MULTIPATH",
    "BLK_DEV_DM",
    "BLK_DEV_MD"
  ],
  "FocusSymbols": [
    "blk_set_mpath_head_limits",
    "blk_stack_mpath_limits",
    "blk_stack_limits"
  ],
  "Reasoning": "The patch refactors block layer queue limits stacking, introducing new functions for multipath devices (like NVMe multipath) and modifying the generic `blk_stack_limits` used by stacking drivers like DM and MD. These are core block layer and NVMe host changes that can be reached in a virtualized environment (e.g., QEMU with NVMe emulation).",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "BLK_DEV_NVME",
    "NVME_MULTIPATH",
    "BLK_DEV_DM",
    "BLK_DEV_MD"
  ],
  "FocusSymbols": [
    "blk_set_mpath_head_limits",
    "blk_stack_mpath_limits",
    "blk_stack_limits"
  ],
  "Reasoning": "The patch refactors block layer queue limits stacking, introducing new functions for multipath devices (like NVMe multipath) and modifying the generic `blk_stack_limits` used by stacking drivers like DM and MD. These are core block layer and NVMe host changes that can be reached in a virtualized environment (e.g., QEMU with NVMe emulation).",
  "WorthFuzzing": true
}

21/1 2026/08/21 11:11 action If
0m Args:
{
  "WorthFuzzing": true
}

Results:
null

22/2 2026/08/21 11:11 agent kmsan-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "KMSANReasoning": "The patch modifies block layer settings (`blk-settings.c`) and NVMe multipath queue limits stacking (`nvme/host/core.c`, `nvme/host/multipath.c`). It introduces new helper functions (`blk_set_mpath_head_limits`, `blk_stack_mpath_limits`) to properly stack block limits for multipath devices. The structures modified (`struct queue_limits`) are fully initialized by copying existing limits (e.g., via `queue_limits_start_update()`) before being modified and committed. No uninitialized memory is introduced, read, or leaked to user space. The changes are purely logic refactoring and fixing how block limits are stacked. Therefore, a dedicated KMSAN fuzzing session is not justified, as there are no uninitialized memory risks. Standard KASAN and other bug detectors are sufficient to catch any potential memory corruption or logic bugs.",
  "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 fb9299a0c733871f1b8e539c92d7b3a24a014028
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Aug 21 11:10:18 2026 +0000

    syz-cluster: applied patch under review

diff --git a/block/blk-settings.c b/block/blk-settings.c
index 8274631290dbf..8f70bab0a814a 100644
--- a/block/blk-settings.c
+++ b/block/blk-settings.c
@@ -653,6 +653,14 @@ static unsigned int blk_round_down_sectors(unsigned int sectors, unsigned int lb
 	return sectors;
 }
 
+static void blk_clear_atomic_write_limits(struct queue_limits *lim)
+{
+	lim->atomic_write_hw_max = 0;
+	lim->atomic_write_hw_unit_max = 0;
+	lim->atomic_write_hw_unit_min = 0;
+	lim->atomic_write_hw_boundary = 0;
+}
+
 /* Check if second and later bottom devices are compliant */
 static bool blk_stack_atomic_writes_tail(struct queue_limits *t,
 				struct queue_limits *b)
@@ -726,8 +734,8 @@ static bool blk_stack_atomic_writes_head(struct queue_limits *t,
 	return true;
 }
 
-static void blk_stack_atomic_writes_limits(struct queue_limits *t,
-				struct queue_limits *b, sector_t start)
+static bool blk_stack_atomic_writes_hw_limits(struct queue_limits *t,
+				struct queue_limits *b)
 {
 	if (!(b->features & BLK_FEAT_ATOMIC_WRITES))
 		goto unsupported;
@@ -735,9 +743,6 @@ static void blk_stack_atomic_writes_limits(struct queue_limits *t,
 	if (!b->atomic_write_hw_unit_min)
 		goto unsupported;
 
-	if (!blk_atomic_write_start_sect_aligned(start, b))
-		goto unsupported;
-
 	/* UINT_MAX indicates no stacking of bottom devices yet */
 	if (t->atomic_write_hw_max == UINT_MAX) {
 		if (!blk_stack_atomic_writes_head(t, b))
@@ -747,49 +752,32 @@ static void blk_stack_atomic_writes_limits(struct queue_limits *t,
 			goto unsupported;
 	}
 	blk_stack_atomic_writes_chunk_sectors(t);
-	return;
+	return true;
 
 unsupported:
-	t->atomic_write_hw_max = 0;
-	t->atomic_write_hw_unit_max = 0;
-	t->atomic_write_hw_unit_min = 0;
-	t->atomic_write_hw_boundary = 0;
+	blk_clear_atomic_write_limits(t);
+	return false;
 }
 
-/**
- * blk_stack_limits - adjust queue_limits for stacked devices
- * @t:	the stacking driver limits (top device)
- * @b:  the underlying queue limits (bottom, component device)
- * @start:  first data sector within component device
- *
- * Description:
- *    This function is used by stacking drivers like MD and DM to ensure
- *    that all component devices have compatible block sizes and
- *    alignments.  The stacking driver must provide a queue_limits
- *    struct (top) and then iteratively call the stacking function for
- *    all component (bottom) devices.  The stacking function will
- *    attempt to combine the values and ensure proper alignment.
- *
- *    Returns 0 if the top and bottom queue_limits are compatible.  The
- *    top device's block sizes and alignment offsets may be adjusted to
- *    ensure alignment with the bottom device. If no compatible sizes
- *    and alignments exist, -1 is returned and the resulting top
- *    queue_limits will have the misaligned flag set to indicate that
- *    the alignment_offset is undefined.
- */
-int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
-		     sector_t start)
+static void blk_stack_atomic_writes_limits(struct queue_limits *t,
+				struct queue_limits *b, sector_t start)
 {
-	unsigned int top, bottom, alignment;
-	int ret = 0;
+	if (blk_stack_atomic_writes_hw_limits(t, b) &&
+	    !blk_atomic_write_start_sect_aligned(start, b))
+		blk_clear_atomic_write_limits(t);
+}
 
-	t->features |= (b->features & BLK_FEAT_INHERIT_MASK);
+#define STACK_MIN_NOT_ZERO(t, b, field) \
+	((t)->field = min_not_zero((t)->field, (b)->field))
+#define STACK_MIN(t, b, field) \
+	((t)->field = min((t)->field, (b)->field))
 
+static void blk_stack_path_limits(struct queue_limits *t,
+		const struct queue_limits *b)
+{
 	/*
-	 * Some feaures need to be supported both by the stacking driver and all
-	 * underlying devices.  The stacking driver sets these flags before
-	 * stacking the limits, and this will clear the flags if any of the
-	 * underlying devices does not support it.
+	 * These features must be supported by the top queue and every path that
+	 * can execute I/O. Clear them when a path does not support them.
 	 */
 	if (!(b->features & BLK_FEAT_NOWAIT))
 		t->features &= ~BLK_FEAT_NOWAIT;
@@ -798,51 +786,102 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
 	if (!(b->features & BLK_FEAT_PCI_P2PDMA))
 		t->features &= ~BLK_FEAT_PCI_P2PDMA;
 
-	t->flags |= (b->flags & BLK_FLAG_MISALIGNED);
+	STACK_MIN_NOT_ZERO(t, b, max_hw_sectors);
+	STACK_MIN_NOT_ZERO(t, b, max_dev_sectors);
+	STACK_MIN_NOT_ZERO(t, b, seg_boundary_mask);
+	STACK_MIN_NOT_ZERO(t, b, virt_boundary_mask);
+	STACK_MIN_NOT_ZERO(t, b, max_segments);
+	STACK_MIN_NOT_ZERO(t, b, max_integrity_segments);
+	STACK_MIN_NOT_ZERO(t, b, max_segment_size);
+	STACK_MIN(t, b, max_write_zeroes_sectors);
+	STACK_MIN(t, b, max_hw_wzeroes_unmap_sectors);
+	STACK_MIN_NOT_ZERO(t, b, max_discard_segments);
+	STACK_MIN(t, b, max_hw_zone_append_sectors);
+	t->dma_alignment = max(t->dma_alignment, b->dma_alignment);
+}
 
-	t->max_sectors = min_not_zero(t->max_sectors, b->max_sectors);
-	t->max_user_sectors = min_not_zero(t->max_user_sectors,
-			b->max_user_sectors);
-	t->max_hw_sectors = min_not_zero(t->max_hw_sectors, b->max_hw_sectors);
-	t->max_dev_sectors = min_not_zero(t->max_dev_sectors, b->max_dev_sectors);
-	t->max_write_zeroes_sectors = min(t->max_write_zeroes_sectors,
-					b->max_write_zeroes_sectors);
-	t->max_user_wzeroes_unmap_sectors =
-			min(t->max_user_wzeroes_unmap_sectors,
-			    b->max_user_wzeroes_unmap_sectors);
-	t->max_hw_wzeroes_unmap_sectors =
-			min(t->max_hw_wzeroes_unmap_sectors,
-			    b->max_hw_wzeroes_unmap_sectors);
+/**
+ * blk_set_mpath_head_limits - set head limits common to all paths
+ * @t: limits for the multipath head
+ * @b: limits for one path
+ *
+ * Set head limits that are expected to be identical across paths. Stack
+ * limits that may differ between paths with blk_stack_mpath_limits().
+ */
+void blk_set_mpath_head_limits(struct queue_limits *t,
+			       struct queue_limits *b)
+{
+	t->logical_block_size = b->logical_block_size;
+	t->physical_block_size = b->physical_block_size;
+	t->alignment_offset = b->alignment_offset;
+	t->io_min = b->io_min;
+	t->io_opt = b->io_opt;
+	t->discard_granularity = b->discard_granularity;
+	t->discard_alignment = b->discard_alignment;
+	t->zone_write_granularity = b->zone_write_granularity;
+	t->max_write_streams = b->max_write_streams;
+	t->write_stream_granularity = b->write_stream_granularity;
+}
+EXPORT_SYMBOL_GPL(blk_set_mpath_head_limits);
 
-	t->max_hw_zone_append_sectors = min(t->max_hw_zone_append_sectors,
-					b->max_hw_zone_append_sectors);
+/**
+ * blk_stack_mpath_limits - stack limits across same-LBA multipath paths
+ * @t: limits for the multipath head
+ * @b: limits for one path
+ *
+ * Stack limits in @b that may differ between paths. Unlike
+ * blk_stack_limits(), this does not apply mapped-range topology or a mapping
+ * offset. Set limits that are expected to be identical across paths with
+ * blk_set_mpath_head_limits().
+ *
+ * Initialize @t with blk_set_stacking_limits() and set features that require
+ * support from every path before the first call. Set
+ * @t->max_hw_discard_sectors to UINT_MAX and call once for each path. A zero
+ * discard limit disables discard for the head.
+ */
+void blk_stack_mpath_limits(struct queue_limits *t, struct queue_limits *b)
+{
+	if (b->chunk_sectors)
+		t->chunk_sectors = gcd(t->chunk_sectors, b->chunk_sectors);
 
-	t->seg_boundary_mask = min_not_zero(t->seg_boundary_mask,
-					    b->seg_boundary_mask);
-	t->virt_boundary_mask = min_not_zero(t->virt_boundary_mask,
-					    b->virt_boundary_mask);
+	t->features |= b->features &
+		(BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA |
+		 BLK_FEAT_ROTATIONAL | BLK_FEAT_STABLE_WRITES);
+	blk_stack_path_limits(t, b);
+	STACK_MIN(t, b, max_hw_discard_sectors);
+	blk_stack_atomic_writes_hw_limits(t, b);
+
+	if (t->features & BLK_FEAT_ZONED) {
+		STACK_MIN_NOT_ZERO(t, b, max_open_zones);
+		STACK_MIN_NOT_ZERO(t, b, max_active_zones);
+	}
+}
+EXPORT_SYMBOL_GPL(blk_stack_mpath_limits);
 
-	t->max_segments = min_not_zero(t->max_segments, b->max_segments);
-	t->max_discard_segments = min_not_zero(t->max_discard_segments,
-					       b->max_discard_segments);
-	t->max_integrity_segments = min_not_zero(t->max_integrity_segments,
-						 b->max_integrity_segments);
+/*
+ * Stack block sizes, I/O granularities, chunk boundaries and alignment for a
+ * bottom-device range mapped at @start. Round maximum sector limits after the
+ * resulting logical block size is known.
+ */
+static int blk_stack_topology_limits(struct queue_limits *t,
+		const struct queue_limits *b, sector_t start)
+{
+	unsigned int top, bottom, alignment;
+	int ret = 0;
 
-	t->max_segment_size = min_not_zero(t->max_segment_size,
-					   b->max_segment_size);
+	t->flags |= b->flags & BLK_FLAG_MISALIGNED;
 
 	alignment = queue_limit_alignment_offset(b, start);
 
-	/* Bottom device has different alignment.  Check that it is
+	/*
+	 * The bottom device has a different alignment. Check that it is
 	 * compatible with the current top alignment.
 	 */
 	if (t->alignment_offset != alignment) {
-
-		top = max(t->physical_block_size, t->io_min)
-			+ t->alignment_offset;
+		top = max(t->physical_block_size, t->io_min) + t->alignment_offset;
 		bottom = max(b->physical_block_size, b->io_min) + alignment;
 
-		/* Verify that top and bottom intervals line up */
+		/* Verify that top and bottom intervals line up. */
 		if (max(top, bottom) % min(top, bottom)) {
 			t->flags |= BLK_FLAG_MISALIGNED;
 			ret = -1;
@@ -851,15 +890,12 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
 
 	t->logical_block_size = max(t->logical_block_size,
 				    b->logical_block_size);
-
 	t->physical_block_size = max(t->physical_block_size,
 				     b->physical_block_size);
-
 	t->io_min = max(t->io_min, b->io_min);
 	t->io_opt = lcm_not_zero(t->io_opt, b->io_opt);
-	t->dma_alignment = max(t->dma_alignment, b->dma_alignment);
 
-	/* Set non-power-of-2 compatible chunk_sectors boundary */
+	/* Set non-power-of-2 compatible chunk_sectors boundary. */
 	if (b->chunk_sectors)
 		t->chunk_sectors = gcd(t->chunk_sectors, b->chunk_sectors);
 
@@ -891,19 +927,64 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
 		ret = -1;
 	}
 
-	/* Find lowest common alignment_offset */
-	t->alignment_offset = lcm_not_zero(t->alignment_offset, alignment)
-		% max(t->physical_block_size, t->io_min);
+	/* Find lowest common alignment_offset. */
+	t->alignment_offset = lcm_not_zero(t->alignment_offset, alignment) %
+		max(t->physical_block_size, t->io_min);
 
-	/* Verify that new alignment_offset is on a logical block boundary */
+	/* Verify that new alignment_offset is on a logical block boundary. */
 	if (t->alignment_offset & (t->logical_block_size - 1)) {
 		t->flags |= BLK_FLAG_MISALIGNED;
 		ret = -1;
 	}
 
-	t->max_sectors = blk_round_down_sectors(t->max_sectors, t->logical_block_size);
-	t->max_hw_sectors = blk_round_down_sectors(t->max_hw_sectors, t->logical_block_size);
-	t->max_dev_sectors = blk_round_down_sectors(t->max_dev_sectors, t->logical_block_size);
+	t->max_sectors = blk_round_down_sectors(t->max_sectors,
+						t->logical_block_size);
+	t->max_hw_sectors = blk_round_down_sectors(t->max_hw_sectors,
+						   t->logical_block_size);
+	t->max_dev_sectors = blk_round_down_sectors(t->max_dev_sectors,
+						    t->logical_block_size);
+
+	return ret;
+}
+
+/**
+ * blk_stack_limits - adjust queue_limits for stacked devices
+ * @t:	the stacking driver limits (top device)
+ * @b:  the underlying queue limits (bottom, component device)
+ * @start:  first data sector within component device
+ *
+ * Description:
+ *    This function is used by stacking drivers like MD and DM to ensure
+ *    that all component devices have compatible block sizes and
+ *    alignments.  The stacking driver must provide a queue_limits
+ *    struct (top) and then iteratively call the stacking function for
+ *    all component (bottom) devices.  The stacking function will
+ *    attempt to combine the values and ensure proper alignment.
+ *
+ *    Returns 0 if the top and bottom queue_limits are compatible.  The
+ *    top device's block sizes and alignment offsets may be adjusted to
+ *    ensure alignment with the bottom device. If no compatible sizes
+ *    and alignments exist, -1 is returned and the resulting top
+ *    queue_limits will have the misaligned flag set to indicate that
+ *    the alignment_offset is undefined.
+ */
+int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
+		     sector_t start)
+{
+	unsigned int alignment;
+	int ret;
+
+	t->features |= (b->features & BLK_FEAT_INHERIT_MASK);
+	blk_stack_path_limits(t, b);
+
+	t->max_sectors = min_not_zero(t->max_sectors, b->max_sectors);
+	t->max_user_sectors = min_not_zero(t->max_user_sectors,
+			b->max_user_sectors);
+	t->max_user_wzeroes_unmap_sectors =
+			min(t->max_user_wzeroes_unmap_sectors,
+			    b->max_user_wzeroes_unmap_sectors);
+
+	ret = blk_stack_topology_limits(t, b, start);
 
 	/* Discard alignment and granularity */
 	if (b->discard_granularity) {
@@ -911,8 +992,9 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
 
 		t->max_discard_sectors = min_not_zero(t->max_discard_sectors,
 						      b->max_discard_sectors);
-		t->max_hw_discard_sectors = min_not_zero(t->max_hw_discard_sectors,
-							 b->max_hw_discard_sectors);
+		t->max_hw_discard_sectors =
+			min_not_zero(t->max_hw_discard_sectors,
+				     b->max_hw_discard_sectors);
 		t->discard_granularity = max(t->discard_granularity,
 					     b->discard_granularity);
 		t->discard_alignment = lcm_not_zero(t->discard_alignment, alignment) %
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 1322c678f4eb8..a16986ec1c8ef 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -2530,12 +2530,35 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
 	return ret;
 }
 
-static void nvme_stack_zone_resources(struct queue_limits *t,
-				      const struct queue_limits *b)
+static int nvme_update_ns_head_limits(struct nvme_ns *ns,
+		struct nvme_ns_info *info, bool unsupported)
 {
-	t->max_open_zones = min_not_zero(t->max_open_zones, b->max_open_zones);
-	t->max_active_zones =
-		min_not_zero(t->max_active_zones, b->max_active_zones);
+	struct queue_limits *ns_lim = &ns->disk->queue->limits;
+	struct request_queue *head_q = ns->head->disk->queue;
+	struct queue_limits lim;
+	unsigned int memflags;
+	int ret;
+
+	lim = queue_limits_start_update(head_q);
+	memflags = blk_mq_freeze_queue(head_q);
+	blk_set_mpath_head_limits(&lim, ns_lim);
+	blk_stack_mpath_limits(&lim, ns_lim);
+	if (unsupported)
+		ns->head->disk->flags |= GENHD_FL_HIDDEN;
+	else
+		nvme_init_integrity(ns->head, &lim, info);
+	ret = queue_limits_commit_update(head_q, &lim);
+	if (ret)
+		goto unfreeze_head_queue;
+
+	set_capacity_and_notify(ns->head->disk, get_capacity(ns->disk));
+	set_disk_ro(ns->head->disk, nvme_ns_is_readonly(ns, info));
+	nvme_mpath_revalidate_paths(ns->head);
+	ret = nvme_mpath_revalidate_zones(ns->head);
+
+unfreeze_head_queue:
+	blk_mq_unfreeze_queue(head_q, memflags);
+	return ret;
 }
 
 static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)
@@ -2576,54 +2599,8 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)
 		ret = 0;
 	}
 
-	if (!ret && nvme_ns_head_multipath(ns->head)) {
-		struct queue_limits *ns_lim = &ns->disk->queue->limits;
-		struct queue_limits lim;
-		unsigned int memflags;
-
-		lim = queue_limits_start_update(ns->head->disk->queue);
-		memflags = blk_mq_freeze_queue(ns->head->disk->queue);
-		/*
-		 * queue_limits mixes values that are the hardware limitations
-		 * for bio splitting with what is the device configuration.
-		 *
-		 * For NVMe the device configuration can change after e.g. a
-		 * Format command, and we really want to pick up the new format
-		 * value here.  But we must still stack the queue limits to the
-		 * least common denominator for multipathing to split the bios
-		 * properly.
-		 *
-		 * To work around this, we explicitly set the device
-		 * configuration to those that we just queried, but only stack
-		 * the splitting limits in to make sure we still obey possibly
-		 * lower limitations of other controllers.
-		 */
-		lim.logical_block_size = ns_lim->logical_block_size;
-		lim.physical_block_size = ns_lim->physical_block_size;
-		lim.io_min = ns_lim->io_min;
-		lim.io_opt = ns_lim->io_opt;
-		queue_limits_stack_bdev(&lim, ns->disk->part0, 0,
-					ns->head->disk->disk_name);
-		if (lim.features & BLK_FEAT_ZONED)
-			nvme_stack_zone_resources(&lim, ns_lim);
-		if (unsupported)
-			ns->head->disk->flags |= GENHD_FL_HIDDEN;
-		else
-			nvme_init_integrity(ns->head, &lim, info);
-		lim.max_write_streams = ns_lim->max_write_streams;
-		lim.write_stream_granularity = ns_lim->write_stream_granularity;
-		ret = queue_limits_commit_update(ns->head->disk->queue, &lim);
-		if (ret)
-			goto unfreeze_head_queue;
-
-		set_capacity_and_notify(ns->head->disk, get_capacity(ns->disk));
-		set_disk_ro(ns->head->disk, nvme_ns_is_readonly(ns, info));
-		nvme_mpath_revalidate_paths(ns->head);
-		ret = nvme_mpath_revalidate_zones(ns->head);
-
-unfreeze_head_queue:
-		blk_mq_unfreeze_queue(ns->head->disk->queue, memflags);
-	}
+	if (!ret && nvme_ns_head_multipath(ns->head))
+		ret = nvme_update_ns_head_limits(ns, info, unsupported);
 
 	return ret;
 }
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 75dbb58286a32..448c7f33c6874 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -760,6 +760,8 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
 		return 0;
 
 	blk_set_stacking_limits(&lim);
+	/* No path discard limit has been stacked yet. */
+	lim.max_hw_discard_sectors = UINT_MAX;
 	lim.dma_alignment = 3;
 	lim.features |= BLK_FEAT_IO_STAT | BLK_FEAT_NOWAIT |
 		BLK_FEAT_POLL | BLK_FEAT_ATOMIC_WRITES | BLK_FEAT_PCI_P2PDMA;
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 9213a5716f95a..2fe711c196c9e 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1153,6 +1153,9 @@ static inline void blk_queue_disable_write_zeroes(struct request_queue *q)
  */
 extern void blk_set_queue_depth(struct request_queue *q, unsigned int depth);
 extern void blk_set_stacking_limits(struct queue_limits *lim);
+void blk_set_mpath_head_limits(struct queue_limits *t,
+			       struct queue_limits *b);
+void blk_stack_mpath_limits(struct queue_limits *t, struct queue_limits *b);
 extern int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
 			    sector_t offset);
 void queue_limits_stack_bdev(struct queue_limits *t, struct block_device *bdev,

23/3 2026/08/21 11:11 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 8797
							output: 21
							thoughts: 721
						

Thoughts:
**KMSAN Fuzzing Session Assessment**

Okay, so I need to decide if this patch warrants a dedicated KMSAN fuzzing session. It's touching the block layer (`blk-settings.c`) and specifically how queue limits are handled, especially for multipath and block devices. It introduces these new functions: `blk_set_mpath_head_limits`, `blk_stack_mpath_limits`, and it modifies `blk_stack_limits`, along with changes in `nvme/host/core.c` to leverage these new multipath queue limit functions.

Let's dissect this. I see modifications to functions like `blk_clear_atomic_write_limits`, `blk_stack_atomic_writes_hw_limits`, `blk_stack_path_limits`, `blk_set_mpath_head_limits`, `blk_stack_mpath_limits`, `blk_stack_topology_limits`, all feeding into `blk_stack_limits`. The `nvme/host/core.c` change brings in `nvme_update_ns_head_limits`, which leverages the new functions, and in `nvme/host/multipath.c`, I see a setting of `lim.max_hw_discard_sectors = UINT_MAX;` before stacking.

My immediate concern is always uninitialized memory. Does this change the way `struct queue_limits` is populated or stacked in a way that could lead to reads of uninitialized data or data leaks? From the code, it looks like `struct queue_limits` is usually stack-allocated, as in `nvme_update_ns_head_limits`:

```c
	struct queue_limits lim;
	unsigned int memflags;
	int ret;

	lim = queue_limits_start_update(head_q);
```

Crucially, `queue_limits_start_update` returns a copy of the *current* queue limits:

```c
struct queue_limits queue_limits_start_update(struct request_queue *q)
{
	return q->limits;
}
```

So `lim` is initialized with existing values. Then `blk_set_mpath_head_limits` and `blk_stack_mpath_limits` modify `lim`, and finally, `queue_limits_commit_update` applies the changes.

There doesn't seem to be any opportunity for uninitialized memory reads or leaks to user space here. The changes are really about refactoring and ensuring the correct stacking of block limits for multipath devices. It's all about internal logic and data structure manipulation. Nothing new is being exposed to user space, so no uninitialized memory could be potentially exposed.

Just to be absolutely sure, I'll quickly check the `queue_limits_start_update` function in relation to other usages, using `default_api:grepper`.

**Conclusion:** Based on this analysis, I'm confident that a dedicated KMSAN session isn't needed here.



24/3 2026/08/21 11:11 tool grepper
0m Args:
{
  "Expression": "queue_limits_start_update"
}

Results:
{
  "Output": "block/blk-integrity.c=226=static ssize_t flag_store(struct device *dev, const char *page, size_t count,\n--\nblock/blk-integrity.c-238-\t/* note that the flags are inverted vs the values in the sysfs files */\nblock/blk-integrity.c:239:\tlim = queue_limits_start_update(q);\nblock/blk-integrity.c-240-\tif (val)\n--\nblock/blk-settings.c=522=int blk_set_default_limits(struct queue_limits *lim)\n--\nblock/blk-settings.c-538- *\nblock/blk-settings.c:539: * Apply the limits in @lim that were obtained from queue_limits_start_update()\nblock/blk-settings.c-540- * and updated by the caller to @q.  The caller must have frozen the queue or\n--\nblock/blk-settings.c=571=EXPORT_SYMBOL_GPL(queue_limits_commit_update);\n--\nblock/blk-settings.c-577- *\nblock/blk-settings.c:578: * Apply the limits in @lim that were obtained from queue_limits_start_update()\nblock/blk-settings.c-579- * and updated with the new values by the caller to @q.  Freezes the queue\n--\nblock/blk-settings.c=596=EXPORT_SYMBOL_GPL(queue_limits_commit_update_frozen);\n--\nblock/blk-settings.c-603- * Apply the limits in @lim that were freshly initialized to @q.\nblock/blk-settings.c:604: * To update existing limits use queue_limits_start_update() and\nblock/blk-settings.c-605- * queue_limits_commit_update() instead.\n--\nblock/blk-sysfs.c=879=queue_attr_store(struct kobject *kobj, struct attribute *attr,\n--\nblock/blk-sysfs.c-891-\nblock/blk-sysfs.c:892:\t\tstruct queue_limits lim = queue_limits_start_update(q);\nblock/blk-sysfs.c-893-\n--\nblock/blk-zoned.c=2088=static int disk_update_zone_resources(struct gendisk *disk,\n--\nblock/blk-zoned.c-2096-\nblock/blk-zoned.c:2097:\tlim = queue_limits_start_update(q);\nblock/blk-zoned.c-2098-\n--\ndrivers/block/drbd/drbd_nl.c=1301=void drbd_reconsider_queue_parameters(struct drbd_device *device,\n--\ndrivers/block/drbd/drbd_nl.c-1334-\ndrivers/block/drbd/drbd_nl.c:1335:\tlim = queue_limits_start_update(q);\ndrivers/block/drbd/drbd_nl.c-1336-\tif (bdev) {\n--\ndrivers/block/loop.c=224=static void loop_clear_limits(struct loop_device *lo, int mode)\ndrivers/block/loop.c-225-{\ndrivers/block/loop.c:226:\tstruct queue_limits lim = queue_limits_start_update(lo-\u003elo_queue);\ndrivers/block/loop.c-227-\n--\ndrivers/block/loop.c=977=static int loop_configure(struct loop_device *lo, blk_mode_t mode,\n--\ndrivers/block/loop.c-1056-\ndrivers/block/loop.c:1057:\tlim = queue_limits_start_update(lo-\u003elo_queue);\ndrivers/block/loop.c-1058-\tloop_update_limits(lo, \u0026lim, config-\u003eblock_size);\n--\ndrivers/block/loop.c=1111=static void __loop_clr_fd(struct loop_device *lo)\n--\ndrivers/block/loop.c-1132-\t */\ndrivers/block/loop.c:1133:\tlim = queue_limits_start_update(lo-\u003elo_queue);\ndrivers/block/loop.c-1134-\tlim.logical_block_size = SECTOR_SIZE;\n--\ndrivers/block/loop.c=1441=static int loop_set_block_size(struct loop_device *lo, blk_mode_t mode,\n--\ndrivers/block/loop.c-1472-\ndrivers/block/loop.c:1473:\tlim = queue_limits_start_update(lo-\u003elo_queue);\ndrivers/block/loop.c-1474-\tloop_update_limits(lo, \u0026lim, arg);\n--\ndrivers/block/nbd.c=334=static int nbd_set_size(struct nbd_device *nbd, loff_t bytesize, loff_t blksize)\n--\ndrivers/block/nbd.c-353-\ndrivers/block/nbd.c:354:\tlim = queue_limits_start_update(nbd-\u003edisk-\u003equeue);\ndrivers/block/nbd.c-355-\tif (nbd-\u003econfig-\u003eflags \u0026 NBD_FLAG_SEND_TRIM)\n--\ndrivers/block/virtio_blk.c=1094=cache_type_store(struct device *dev, struct device_attribute *attr,\n--\ndrivers/block/virtio_blk.c-1109-\ndrivers/block/virtio_blk.c:1110:\tlim = queue_limits_start_update(disk-\u003equeue);\ndrivers/block/virtio_blk.c-1111-\tif (virtblk_get_cache_mode(vdev))\n--\ndrivers/block/xen-blkfront.c=2006=static int blkif_recover(struct blkfront_info *info)\n--\ndrivers/block/xen-blkfront.c-2014-\ndrivers/block/xen-blkfront.c:2015:\tlim = queue_limits_start_update(info-\u003erq);\ndrivers/block/xen-blkfront.c-2016-\tblkfront_gather_backend_features(info);\n--\ndrivers/md/dm-table.c=2028=int dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,\n--\ndrivers/md/dm-table.c-2082-\ndrivers/md/dm-table.c:2083:\told_limits = queue_limits_start_update(q);\ndrivers/md/dm-table.c-2084-\tr = queue_limits_commit_update(q, limits);\n--\ndrivers/md/md.c=5959=static int mddev_set_logical_block_size(struct mddev *mddev,\n--\ndrivers/md/md.c-5970-\ndrivers/md/md.c:5971:\tlim = queue_limits_start_update(mddev-\u003egendisk-\u003equeue);\ndrivers/md/md.c-5972-\tlim.logical_block_size = lbs;\n--\ndrivers/md/md.c=6228=int mddev_stack_new_rdev(struct mddev *mddev, struct md_rdev *rdev)\n--\ndrivers/md/md.c-6241-\ndrivers/md/md.c:6242:\tlim = queue_limits_start_update(mddev-\u003egendisk-\u003equeue);\ndrivers/md/md.c-6243-\tqueue_limits_stack_bdev(\u0026lim, rdev-\u003ebdev, rdev-\u003edata_offset,\n--\ndrivers/md/md.c=6258=void mddev_update_io_opt(struct mddev *mddev, unsigned int nr_stripes)\n--\ndrivers/md/md.c-6267-\t\treturn;\ndrivers/md/md.c:6268:\tlim = queue_limits_start_update(mddev-\u003egendisk-\u003equeue);\ndrivers/md/md.c-6269-\tlim.io_opt = lim.io_min * nr_stripes;\n--\ndrivers/md/raid5.c=7172=raid5_store_skip_copy(struct mddev *mddev, const char *page, size_t len)\n--\ndrivers/md/raid5.c-7191-\t\tstruct request_queue *q = mddev-\u003egendisk-\u003equeue;\ndrivers/md/raid5.c:7192:\t\tstruct queue_limits lim = queue_limits_start_update(q);\ndrivers/md/raid5.c-7193-\n--\ndrivers/nvme/host/core.c=2238=static int nvme_update_ns_info_generic(struct nvme_ns *ns,\n--\ndrivers/nvme/host/core.c-2244-\ndrivers/nvme/host/core.c:2245:\tlim = queue_limits_start_update(ns-\u003edisk-\u003equeue);\ndrivers/nvme/host/core.c-2246-\tnvme_set_ctrl_limits(ns-\u003ectrl, \u0026lim, false);\n--\ndrivers/nvme/host/core.c=2408=static int nvme_update_ns_info_block(struct nvme_ns *ns,\n--\ndrivers/nvme/host/core.c-2459-\ndrivers/nvme/host/core.c:2460:\tlim = queue_limits_start_update(ns-\u003edisk-\u003equeue);\ndrivers/nvme/host/core.c-2461-\n--\ndrivers/nvme/host/core.c=2533=static int nvme_update_ns_head_limits(struct nvme_ns *ns,\n--\ndrivers/nvme/host/core.c-2541-\ndrivers/nvme/host/core.c:2542:\tlim = queue_limits_start_update(head_q);\ndrivers/nvme/host/core.c-2543-\tmemflags = blk_mq_freeze_queue(head_q);\n--\ndrivers/nvme/host/core.c=3594=static int nvme_init_identify(struct nvme_ctrl *ctrl)\n--\ndrivers/nvme/host/core.c-3662-\ndrivers/nvme/host/core.c:3663:\tlim = queue_limits_start_update(ctrl-\u003eadmin_q);\ndrivers/nvme/host/core.c-3664-\tnvme_set_ctrl_limits(ctrl, \u0026lim, true);\n--\ndrivers/s390/block/dasd.c=297=static int dasd_state_basic_to_ready(struct dasd_device *device)\n--\ndrivers/s390/block/dasd.c-319-\ndrivers/s390/block/dasd.c:320:\tlim = queue_limits_start_update(block-\u003egdp-\u003equeue);\ndrivers/s390/block/dasd.c-321-\tlim.max_dev_sectors = device-\u003ediscipline-\u003emax_sectors(block);\n--\ndrivers/scsi/mpi3mr/mpi3mr_os.c=1067=mpi3mr_update_sdev(struct scsi_device *sdev, void *data)\n--\ndrivers/scsi/mpi3mr/mpi3mr_os.c-1077-\ndrivers/scsi/mpi3mr/mpi3mr_os.c:1078:\tlim = queue_limits_start_update(sdev-\u003erequest_queue);\ndrivers/scsi/mpi3mr/mpi3mr_os.c-1079-\tmpi3mr_configure_tgt_dev(tgtdev, \u0026lim);\n--\ndrivers/scsi/scsi_scan.c=875=static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,\n--\ndrivers/scsi/scsi_scan.c-1078-\t */\ndrivers/scsi/scsi_scan.c:1079:\tlim = queue_limits_start_update(sdev-\u003erequest_queue);\ndrivers/scsi/scsi_scan.c-1080-\tif (*bflags \u0026 BLIST_MAX_512)\n--\ndrivers/scsi/sd.c=220=cache_type_store(struct device *dev, struct device_attribute *attr,\n--\ndrivers/scsi/sd.c-258-\ndrivers/scsi/sd.c:259:\t\tlim = queue_limits_start_update(sdkp-\u003edisk-\u003equeue);\ndrivers/scsi/sd.c-260-\t\tsd_set_flush_flag(sdkp, \u0026lim);\n--\ndrivers/scsi/sd.c=575=provisioning_mode_store(struct device *dev, struct device_attribute *attr,\n--\ndrivers/scsi/sd.c-592-\ndrivers/scsi/sd.c:593:\tlim = queue_limits_start_update(sdkp-\u003edisk-\u003equeue);\ndrivers/scsi/sd.c-594-\tsd_config_discard(sdkp, \u0026lim, mode);\n--\ndrivers/scsi/sd.c=675=max_write_same_blocks_store(struct device *dev, struct device_attribute *attr,\n--\ndrivers/scsi/sd.c-701-\ndrivers/scsi/sd.c:702:\tlim = queue_limits_start_update(sdkp-\u003edisk-\u003equeue);\ndrivers/scsi/sd.c-703-\tsd_config_write_same(sdkp, \u0026lim);\n--\ndrivers/scsi/sd.c=3760=static void sd_revalidate_disk(struct gendisk *disk)\n--\ndrivers/scsi/sd.c-3789-\ndrivers/scsi/sd.c:3790:\t*lim = queue_limits_start_update(sdkp-\u003edisk-\u003equeue);\ndrivers/scsi/sd.c-3791-\n--\ndrivers/scsi/sr.c=476=static int sr_revalidate_disk(struct scsi_cd *cd)\n--\ndrivers/scsi/sr.c-488-\ndrivers/scsi/sr.c:489:\tlim = queue_limits_start_update(q);\ndrivers/scsi/sr.c-490-\tlim.logical_block_size = sector_size;\n--\ndrivers/usb/storage/scsiglue.c=584=static ssize_t max_sectors_store(struct device *dev, struct device_attribute *attr, const char *buf,\n--\ndrivers/usb/storage/scsiglue.c-594-\ndrivers/usb/storage/scsiglue.c:595:\tlim = queue_limits_start_update(sdev-\u003erequest_queue);\ndrivers/usb/storage/scsiglue.c-596-\tlim.max_hw_sectors = ms;\n--\ninclude/linux/blkdev.h=1080=static inline unsigned int blk_boundary_sectors_left(sector_t offset,\n--\ninclude/linux/blkdev.h-1088-/**\ninclude/linux/blkdev.h:1089: * queue_limits_start_update - start an atomic update of queue limits\ninclude/linux/blkdev.h-1090- * @q:\t\tqueue to update\n--\ninclude/linux/blkdev.h=1099=static inline struct queue_limits\ninclude/linux/blkdev.h:1100:queue_limits_start_update(struct request_queue *q)\ninclude/linux/blkdev.h-1101-\t__acquires(\u0026q-\u003elimits_lock)\n--\ninclude/linux/blkdev.h=1112=int blk_validate_limits(struct queue_limits *lim);\n--\ninclude/linux/blkdev.h-1118- * This functions cancels an atomic update of the queue limits started by\ninclude/linux/blkdev.h:1119: * queue_limits_start_update() and should be used when an error occurs after\ninclude/linux/blkdev.h-1120- * starting update.\n"
}

25/3 2026/08/21 11:11 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14245
							output: 215
							thoughts: 392
						

Thoughts:
**My Assessment of the Patch's Memory Safety**

Okay, so I've been looking at this patch and, frankly, I'm not seeing any red flags regarding uninitialized memory. The `queue_limits_start_update` function, as defined in `blkdev.h`, is pretty straightforward. It grabs a copy of the `q->limits` struct by value after locking the `limits_lock`. This means we're dealing with a *copy* of an initialized structure, which is then modified, and presumably copied back with a function like `queue_limits_commit_update`. Since it's a direct struct copy, all the fields are initialized, right from the start.

The patch itself deals with changes to block layer settings and NVMe multipath queue limits, specifically in `blk-settings.c`, `nvme/host/core.c`, and `nvme/host/multipath.c`. It looks like they're introducing new helper functions like `blk_set_mpath_head_limits` and `blk_stack_mpath_limits` to handle how block limits are stacked, particularly for multipath devices. But, again, these functions are working with *initialized* `struct queue_limits` copies. The structures are initialized by calling `queue_limits_start_update()` or equivalent. No uninitialized memory is being introduced or read, and certainly not leaked to user space.

This seems to be mainly about logic refactoring and ensuring the correct stacking of block limits. Therefore, a dedicated KMSAN fuzzing session feels unnecessary here. We're not dealing with the typical uninitialized memory risks that KMSAN is designed to catch. Standard KASAN and other bug detectors should be sufficient to identify any memory corruption or logic errors that might creep in, and the code changes seem fairly contained and well-defined.



26/3 2026/08/21 11:11 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch modifies block layer settings (`blk-settings.c`) and NVMe multipath queue limits stacking (`nvme/host/core.c`, `nvme/host/multipath.c`). It introduces new helper functions (`blk_set_mpath_head_limits`, `blk_stack_mpath_limits`) to properly stack block limits for multipath devices. The structures modified (`struct queue_limits`) are fully initialized by copying existing limits (e.g., via `queue_limits_start_update()`) before being modified and committed. No uninitialized memory is introduced, read, or leaked to user space. The changes are purely logic refactoring and fixing how block limits are stacked. Therefore, a dedicated KMSAN fuzzing session is not justified, as there are no uninitialized memory risks. Standard KASAN and other bug detectors are sufficient to catch any potential memory corruption or logic bugs.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch modifies block layer settings (`blk-settings.c`) and NVMe multipath queue limits stacking (`nvme/host/core.c`, `nvme/host/multipath.c`). It introduces new helper functions (`blk_set_mpath_head_limits`, `blk_stack_mpath_limits`) to properly stack block limits for multipath devices. The structures modified (`struct queue_limits`) are fully initialized by copying existing limits (e.g., via `queue_limits_start_update()`) before being modified and committed. No uninitialized memory is introduced, read, or leaked to user space. The changes are purely logic refactoring and fixing how block limits are stacked. Therefore, a dedicated KMSAN fuzzing session is not justified, as there are no uninitialized memory risks. Standard KASAN and other bug detectors are sufficient to catch any potential memory corruption or logic bugs.",
  "NeedsKMSAN": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)