FAILED: patch "[PATCH] nvme: skip the zoned limits update if the zone info query" failed to apply to 6.12-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 3838e80fcfb32e62baffb63c6dc0a60153665a4d
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090951-hardhead-splashing-d9e8@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 3838e80fcfb32e62baffb63c6dc0a60153665a4d Mon Sep 17 00:00:00 2001
From: Chao Shi <[email protected]>
Date: Mon, 17 Aug 2026 13:58:59 -0400
Subject: [PATCH] nvme: skip the zoned limits update if the zone info query
failed
nvme_query_zone_info() returns either a negative errno or a positive NVMe status code, but nvme_update_ns_info_block() only tests for the negative case:
ret = nvme_query_zone_info(ns, lbaf, &zi); if (ret < 0) goto out;
If the device fails the Identify Namespace (I/O Command Set specific) command, or the Identify Controller command issued by nvme_set_max_append(), the positive status falls through and setup continues with the zero-initialized zone info. nvme_update_zone_info() then marks the queue zoned with chunk_sectors and ns->head->zsze set to zero.
blk_validate_zoned_limits() does not check chunk_sectors, so the limits commit succeeds. blk_revalidate_disk_zones() does reject the zero zone size, but by then the limits are live and nothing rolls them back, so I/O keeps being submitted to a zoned queue with a zero zone size and disk_zone_no() shifts by ilog2(0):
nvme0n1: Invalid non power of two zone size (0)
UBSAN: shift-out-of-bounds in include/linux/blkdev.h:747:16 shift exponent -1 is negative disk_zone_no include/linux/blkdev.h:747 [inline] bio_straddles_zones include/linux/blkdev.h:1058 [inline] blk_zone_wplug_handle_write block/blk-zoned.c:1423 [inline] blk_zone_plug_bio.cold+0x25/0x1c8 block/blk-zoned.c:1605 blk_mq_submit_bio+0x18fb/0x2870 block/blk-mq.c:3196 submit_bh_wbc+0x575/0x740 fs/buffer.c:2824 __block_write_full_folio+0x728/0xdd0 fs/buffer.c:1933
Any device, firmware or NVMe-oF target that fails this one command reaches this.
Skip the zoned limits update in that case, and log which of the two things happened: during a revalidation the queue keeps the zone geometry it was last validated with, and on a first scan the namespace is registered without zoned limits, so that it is still available as a handle for admin commands. Neither of the paths in nvme_query_zone_info() that return a positive status logs anything, so the failure would otherwise be silent.
zi.zone_size is an exact indicator: every path that returns a positive status returns before it is assigned, and after that the only failure left is -ENODEV, which the caller already handles.
Found by FuzzNvme.
drivers/nvme/host/core.c +19 -2
@@ -2468,9 +2468,26 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,if (!nvme_update_disk_info (ns, id, nvm, &lim)) capacity =0 ; +/* +* A failed zone info query leaves zi zero-initialized, so skip the +* zoned limits update instead of configuring the queue from it. +* During a revalidation that keeps the zone geometry the queue was +* last validated with; on a first scan the namespace is registered +* without zoned limits, so that it is still available as a handle +* for admin commands. +*/ if (IS_ENABLED (CONFIG_BLK_DEV_ZONED ) && - ns->head->ids.csi ==NVME_CSI_ZNS ) -nvme_update_zone_info (ns, &lim, &zi); + ns->head->ids.csi ==NVME_CSI_ZNS ) { +if (zi.zone_size) +nvme_update_zone_info (ns, &lim, &zi); +else +dev_warn (ns->ctrl->device, +"zone info query failed for nsid %u, %s\n" , + ns->head->ns_id, +blk_queue_is_zoned (ns->disk->queue) ? +"keeping the previous zone limits" : +"not enabling zoned mode" ); + }if ((ns->ctrl->vwc &NVME_CTRL_VWC_PRESENT ) && !info->no_vwc) lim.features |=BLK_FEAT_WRITE_CACHE |BLK_FEAT_FUA ;
Sasha Levin
maintainerPATCH 1/4 nvme: all namespaces in a subsystem must adhere to a common atomic write size
From: Alan Adamson <[email protected]>
[ Upstream commit 8695f060a02953b33ac6240895dcb9c7ce16c91c ]
The first namespace configured in a subsystem sets the subsystem's atomic write size based on its AWUPF or NAWUPF. Subsequent namespaces must have an atomic write size (per their AWUPF or NAWUPF) less than or equal to the subsystem's atomic write size, or their probing will be rejected.
Stable-dep-of: 3838e80fcfb3 ("nvme: skip the zoned limits update if the zone info query failed")
--- drivers/nvme/host/core.c | 30 +++++++++++++++++++++++++++--- drivers/nvme/host/nvme.h | 3 ++- 2 files changed, 29 insertions(+), 4 deletions(-)
drivers/nvme/host/core.c +27 -3
@@ -2056,7 +2056,21 @@ static bool nvme_update_disk_info(struct nvme_ns *ns, struct nvme_id_ns *id,if (id->nsfeat &NVME_NS_FEAT_ATOMICS && id->nawupf) atomic_bs = (1 +le16_to_cpu (id->nawupf)) * bs;else - atomic_bs = (1 + ns->ctrl->subsys->awupf) * bs; + atomic_bs = (1 + ns->ctrl->awupf) * bs; + +/* +* Set subsystem atomic bs. +*/ +if (ns->ctrl->subsys->atomic_bs) { +if (atomic_bs != ns->ctrl->subsys->atomic_bs) { +dev_err_ratelimited (ns->ctrl->device, +"%s: Inconsistent Atomic Write Size, Namespace will not be added: Subsystem=%d bytes, Controller/Namespace=%d bytes\n" , + ns->disk ? ns->disk->disk_name :"?" , + ns->ctrl->subsys->atomic_bs, + atomic_bs); + } + }else + ns->ctrl->subsys->atomic_bs = atomic_bs;nvme_update_atomic_write_disk_info (ns, id, lim, bs, atomic_bs); }
@@ -2195,6 +2209,17 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,nvme_set_chunk_sectors (ns, id, &lim);if (!nvme_update_disk_info (ns, id, &lim)) capacity =0 ; + +/* +* Validate the max atomic write size fits within the subsystem's +* atomic write capabilities. +*/ +if (lim.atomic_write_hw_max > ns->ctrl->subsys->atomic_bs) { +blk_mq_unfreeze_queue (ns->disk->queue, memflags); + ret = -ENXIO ; + goto out; + } +nvme_config_discard (ns, &lim);if (IS_ENABLED (CONFIG_BLK_DEV_ZONED ) && ns->head->ids.csi ==NVME_CSI_ZNS )
@@ -3021,7 +3046,6 @@ static int nvme_init_subsystem(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)kfree (subsys);return -EINVAL ; } - subsys->awupf =le16_to_cpu (id->awupf);nvme_mpath_default_iopolicy (subsys); subsys->dev.class = &nvme_subsys_class;
@@ -3431,7 +3455,7 @@ static int nvme_init_identify(struct nvme_ctrl *ctrl)dev_pm_qos_expose_latency_tolerance (ctrl->device); else if (!ctrl->apst_enabled && prev_apst_enabled)dev_pm_qos_hide_latency_tolerance (ctrl->device); - + ctrl->awupf =le16_to_cpu (id->awupf); out_free:kfree (id);return ret;
drivers/nvme/host/nvme.h +2 -1
@@ -410,6 +410,7 @@ struct nvme_ctrl {enum nvme_ctrl_type cntrltype;enum nvme_dctype dctype; +u16 awupf;/* 0's based value. */ };static inline enum nvme_ctrl_state nvme_ctrl_state (struct nvme_ctrl *ctrl)
@@ -442,11 +443,11 @@ struct nvme_subsystem {u8 cmic;enum nvme_subsys_type subtype;u16 vendor_id; -u16 awupf;/* 0's based awupf value. */ struct ida ns_ida;#ifdef CONFIG_NVME_MULTIPATH enum nvme_iopolicy iopolicy;#endif +u32 atomic_bs; };/*
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 nvme: refactor the atomic write unit detection
From: Christoph Hellwig <[email protected]>
[ Upstream commit b2e607fecac15e07f50269c080e2e71b5049dfa2 ]
Move all the code out of nvme_update_disk_info into the helper, and rename the helper to have a somewhat less clumsy name.
Stable adaptation for 6.12:
- Keep the existing atomic write helper refactor, but omit
BLK_FEAT_ATOMIC_WRITES: this tree enables atomic writes through the hardware limits and does not define that feature flag.
- Call the existing discard setup helper from nvme_update_disk_info() after setting logical_block_size. Save the disk-info validity result and defer clearing capacity until after atomic-size validation. This preserves the result while leaving the zoned update insertion point clear for 3838e80fcfb32e62baffb63c6dc0a60153665a4d.
- Adapt the preceding dependency's atomic-size error path to the single-argument blk_mq_unfreeze_queue() API and cancel the pending limits update before unfreezing, releasing limits_lock on rejection.
No additional functions are introduced.
Stable-dep-of: 3838e80fcfb3 ("nvme: skip the zoned limits update if the zone info query failed")
--- drivers/nvme/host/core.c | 84 ++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 38 deletions(-)
drivers/nvme/host/core.c +46 -38
@@ -1995,20 +1995,50 @@ static void nvme_configure_metadata(struct nvme_ctrl *ctrl, } -static void nvme_update_atomic_write_disk_info (struct nvme_ns *ns, -struct nvme_id_ns *id,struct queue_limits *lim, -u32 bs,u32 atomic_bs) +static u32 nvme_configure_atomic_write (struct nvme_ns *ns, +struct nvme_id_ns *id,struct queue_limits *lim,u32 bs) { -unsigned int boundary =0 ; +u32 atomic_bs, boundary =0 ; -if (id->nsfeat &NVME_NS_FEAT_ATOMICS && id->nawupf) { -if (le16_to_cpu (id->nabspf)) +/* +* We do not support an offset for the atomic boundaries. +*/ +if (id->nabo) +return bs; + +if ((id->nsfeat &NVME_NS_FEAT_ATOMICS ) && id->nawupf) { +/* +* Use the per-namespace atomic write unit when available. +*/ + atomic_bs = (1 +le16_to_cpu (id->nawupf)) * bs; +if (id->nabspf) boundary = (le16_to_cpu (id->nabspf) +1 ) * bs; + }else { +/* +* Use the controller wide atomic write unit. This sucks +* because the limit is defined in terms of logical blocks while +* namespaces can have different formats, and because there is +* no clear language in the specification prohibiting different +* values for different controllers in the subsystem. +*/ + atomic_bs = (1 + ns->ctrl->awupf) * bs; + } + +if (!ns->ctrl->subsys->atomic_bs) { + ns->ctrl->subsys->atomic_bs = atomic_bs; + }else if (ns->ctrl->subsys->atomic_bs != atomic_bs) { +dev_err_ratelimited (ns->ctrl->device, +"%s: Inconsistent Atomic Write Size, Namespace will not be added: Subsystem=%d bytes, Controller/Namespace=%d bytes\n" , + ns->disk ? ns->disk->disk_name :"?" , + ns->ctrl->subsys->atomic_bs, + atomic_bs); } + lim->atomic_write_hw_max = atomic_bs; lim->atomic_write_hw_boundary = boundary; lim->atomic_write_hw_unit_min = bs; lim->atomic_write_hw_unit_max =rounddown_pow_of_two (atomic_bs); +return atomic_bs; }static u32 nvme_max_drv_segments (struct nvme_ctrl *ctrl)
@@ -2046,34 +2076,8 @@ static bool nvme_update_disk_info(struct nvme_ns *ns, struct nvme_id_ns *id, valid = false; } - atomic_bs = phys_bs = bs; -if (id->nabo ==0 ) { -/* -* Bit 1 indicates whether NAWUPF is defined for this namespace -* and whether it should be used instead of AWUPF. If NAWUPF == -* 0 then AWUPF must be used instead. -*/ -if (id->nsfeat &NVME_NS_FEAT_ATOMICS && id->nawupf) - atomic_bs = (1 +le16_to_cpu (id->nawupf)) * bs; -else - atomic_bs = (1 + ns->ctrl->awupf) * bs; - -/* -* Set subsystem atomic bs. -*/ -if (ns->ctrl->subsys->atomic_bs) { -if (atomic_bs != ns->ctrl->subsys->atomic_bs) { -dev_err_ratelimited (ns->ctrl->device, -"%s: Inconsistent Atomic Write Size, Namespace will not be added: Subsystem=%d bytes, Controller/Namespace=%d bytes\n" , - ns->disk ? ns->disk->disk_name :"?" , - ns->ctrl->subsys->atomic_bs, - atomic_bs); - } - }else - ns->ctrl->subsys->atomic_bs = atomic_bs; - -nvme_update_atomic_write_disk_info (ns, id, lim, bs, atomic_bs); - } + phys_bs = bs; + atomic_bs =nvme_configure_atomic_write (ns, id, lim, bs);if (id->nsfeat &NVME_NS_FEAT_IO_OPT ) {/* NPWG = Namespace Preferred Write Granularity */
@@ -2097,6 +2101,7 @@ static bool nvme_update_disk_info(struct nvme_ns *ns, struct nvme_id_ns *id, lim->max_write_zeroes_sectors =UINT_MAX ; else lim->max_write_zeroes_sectors = ns->ctrl->max_zeroes_sectors; +nvme_config_discard (ns, lim);return valid; }
@@ -2171,6 +2176,7 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,struct nvme_id_ns *id;sector_t capacity;unsigned lbaf; +bool valid;int ret; ret =nvme_identify_ns (ns->ctrl, info->nsid, &id);
@@ -2207,20 +2213,22 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,nvme_set_ctrl_limits (ns->ctrl, &lim);nvme_configure_metadata (ns->ctrl, ns->head, id, nvm, info);nvme_set_chunk_sectors (ns, id, &lim); -if (!nvme_update_disk_info (ns, id, &lim)) - capacity =0 ; + valid =nvme_update_disk_info (ns, id, &lim);/* * Validate the max atomic write size fits within the subsystem's * atomic write capabilities. */ if (lim.atomic_write_hw_max > ns->ctrl->subsys->atomic_bs) { -blk_mq_unfreeze_queue (ns->disk->queue, memflags); +queue_limits_cancel_update (ns->disk->queue); +blk_mq_unfreeze_queue (ns->disk->queue); ret = -ENXIO ; goto out; } -nvme_config_discard (ns, &lim); +if (!valid) + capacity =0 ; +if (IS_ENABLED (CONFIG_BLK_DEV_ZONED ) && ns->head->ids.csi ==NVME_CSI_ZNS )nvme_update_zone_info (ns, &lim, &zi);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 nvme: fix atomic write size validation
In reply to Sasha LevinFrom: Christoph Hellwig <[email protected]>
[ Upstream commit f46d273449ba65afd53f3dd8fe0182c9df877e08 ]
Don't mix the namespace and controller values, and validate the per-controller limit when probing the controller. This avoid spurious failures for controllers with namespaces that have different namespaces with different logical block sizes, or report the per-namespace values only for some namespaces.
It also fixes a missing queue_limits_cancel_update in an error path by removing that error path.
Stable adaptation for 6.12:
- Remove the atomic-size rejection together with its locally adapted queue_limits_cancel_update() and one-argument queue unfreeze calls. Validation now happens during controller identification, as upstream.
- Keep nvme_config_discard() in nvme_update_disk_info(), where the preceding dependency moved it. Do not restore a duplicate call here.
- Preserve the saved disk-info validity result and capacity handling, leaving the zoned update insertion point available for target 3838e80fcfb32e62baffb63c6dc0a60153665a4d.
No additional functions are introduced.
Stable-dep-of: 3838e80fcfb3 ("nvme: skip the zoned limits update if the zone info query failed")
--- drivers/nvme/host/core.c | 34 +++++++++++----------------------- drivers/nvme/host/nvme.h | 3 +-- 2 files changed, 12 insertions(+), 25 deletions(-)
drivers/nvme/host/core.c +11 -23
@@ -2021,17 +2021,7 @@ static u32 nvme_configure_atomic_write(struct nvme_ns *ns,* no clear language in the specification prohibiting different * values for different controllers in the subsystem. */ - atomic_bs = (1 + ns->ctrl->awupf) * bs; - } - -if (!ns->ctrl->subsys->atomic_bs) { - ns->ctrl->subsys->atomic_bs = atomic_bs; - }else if (ns->ctrl->subsys->atomic_bs != atomic_bs) { -dev_err_ratelimited (ns->ctrl->device, -"%s: Inconsistent Atomic Write Size, Namespace will not be added: Subsystem=%d bytes, Controller/Namespace=%d bytes\n" , - ns->disk ? ns->disk->disk_name :"?" , - ns->ctrl->subsys->atomic_bs, - atomic_bs); + atomic_bs = (1 + ns->ctrl->subsys->awupf) * bs; } lim->atomic_write_hw_max = atomic_bs;
@@ -2215,17 +2205,6 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,nvme_set_chunk_sectors (ns, id, &lim); valid =nvme_update_disk_info (ns, id, &lim); -/* -* Validate the max atomic write size fits within the subsystem's -* atomic write capabilities. -*/ -if (lim.atomic_write_hw_max > ns->ctrl->subsys->atomic_bs) { -queue_limits_cancel_update (ns->disk->queue); -blk_mq_unfreeze_queue (ns->disk->queue); - ret = -ENXIO ; - goto out; - } -if (!valid) capacity =0 ;
@@ -3039,6 +3018,7 @@ static int nvme_init_subsystem(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)memcpy (subsys->model, id->mn,sizeof (subsys->model)); subsys->vendor_id =le16_to_cpu (id->vid); subsys->cmic = id->cmic; + subsys->awupf =le16_to_cpu (id->awupf);/* Versions prior to 1.4 don't necessarily report a valid type */ if (id->cntrltype == NVME_CTRL_DISC ||
@@ -3368,6 +3348,15 @@ static int nvme_init_identify(struct nvme_ctrl *ctrl)if (ret) goto out_free; } + +if (le16_to_cpu (id->awupf) != ctrl->subsys->awupf) { +dev_err_ratelimited (ctrl->device, +"inconsistent AWUPF, controller not added (%u/%u).\n" , +le16_to_cpu (id->awupf), ctrl->subsys->awupf); + ret = -EINVAL ; + goto out_free; + } +memcpy (ctrl->subsys->firmware_rev, id->fr,sizeof (ctrl->subsys->firmware_rev));
@@ -3463,7 +3452,6 @@ static int nvme_init_identify(struct nvme_ctrl *ctrl)dev_pm_qos_expose_latency_tolerance (ctrl->device); else if (!ctrl->apst_enabled && prev_apst_enabled)dev_pm_qos_hide_latency_tolerance (ctrl->device); - ctrl->awupf =le16_to_cpu (id->awupf); out_free:kfree (id);return ret;
drivers/nvme/host/nvme.h +1 -2
@@ -410,7 +410,6 @@ struct nvme_ctrl {enum nvme_ctrl_type cntrltype;enum nvme_dctype dctype; -u16 awupf;/* 0's based value. */ };static inline enum nvme_ctrl_state nvme_ctrl_state (struct nvme_ctrl *ctrl)
@@ -443,11 +442,11 @@ struct nvme_subsystem {u8 cmic;enum nvme_subsys_type subtype;u16 vendor_id; +u16 awupf;/* 0's based value. */ struct ida ns_ida;#ifdef CONFIG_NVME_MULTIPATH enum nvme_iopolicy iopolicy;#endif -u32 atomic_bs; };/*
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 nvme: skip the zoned limits update if the zone info query failed
In reply to Sasha LevinFrom: Chao Shi <[email protected]>
[ Upstream commit 3838e80fcfb32e62baffb63c6dc0a60153665a4d ]
nvme_query_zone_info() returns either a negative errno or a positive NVMe status code, but nvme_update_ns_info_block() only tests for the negative case:
ret = nvme_query_zone_info(ns, lbaf, &zi); if (ret < 0) goto out;
If the device fails the Identify Namespace (I/O Command Set specific) command, or the Identify Controller command issued by nvme_set_max_append(), the positive status falls through and setup continues with the zero-initialized zone info. nvme_update_zone_info() then marks the queue zoned with chunk_sectors and ns->head->zsze set to zero.
blk_validate_zoned_limits() does not check chunk_sectors, so the limits commit succeeds. blk_revalidate_disk_zones() does reject the zero zone size, but by then the limits are live and nothing rolls them back, so I/O keeps being submitted to a zoned queue with a zero zone size and disk_zone_no() shifts by ilog2(0):
nvme0n1: Invalid non power of two zone size (0)
UBSAN: shift-out-of-bounds in include/linux/blkdev.h:747:16 shift exponent -1 is negative disk_zone_no include/linux/blkdev.h:747 [inline] bio_straddles_zones include/linux/blkdev.h:1058 [inline] blk_zone_wplug_handle_write block/blk-zoned.c:1423 [inline] blk_zone_plug_bio.cold+0x25/0x1c8 block/blk-zoned.c:1605 blk_mq_submit_bio+0x18fb/0x2870 block/blk-mq.c:3196 submit_bh_wbc+0x575/0x740 fs/buffer.c:2824 __block_write_full_folio+0x728/0xdd0 fs/buffer.c:1933
Any device, firmware or NVMe-oF target that fails this one command reaches this.
Skip the zoned limits update in that case, and log which of the two things happened: during a revalidation the queue keeps the zone geometry it was last validated with, and on a first scan the namespace is registered without zoned limits, so that it is still available as a handle for admin commands. Neither of the paths in nvme_query_zone_info() that return a positive status logs anything, so the failure would otherwise be silent.
zi.zone_size is an exact indicator: every path that returns a positive status returns before it is assigned, and after that the only failure left is -ENODEV, which the caller already handles.
Found by FuzzNvme.
--- drivers/nvme/host/core.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
drivers/nvme/host/core.c +19 -2
@@ -2208,9 +2208,26 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,if (!valid) capacity =0 ; +/* +* A failed zone info query leaves zi zero-initialized, so skip the +* zoned limits update instead of configuring the queue from it. +* During a revalidation that keeps the zone geometry the queue was +* last validated with; on a first scan the namespace is registered +* without zoned limits, so that it is still available as a handle +* for admin commands. +*/ if (IS_ENABLED (CONFIG_BLK_DEV_ZONED ) && - ns->head->ids.csi ==NVME_CSI_ZNS ) -nvme_update_zone_info (ns, &lim, &zi); + ns->head->ids.csi ==NVME_CSI_ZNS ) { +if (zi.zone_size) +nvme_update_zone_info (ns, &lim, &zi); +else +dev_warn (ns->ctrl->device, +"zone info query failed for nsid %u, %s\n" , + ns->head->ns_id, +blk_queue_is_zoned (ns->disk->queue) ? +"keeping the previous zone limits" : +"not enabling zoned mode" ); + }if (ns->ctrl->vwc &NVME_CTRL_VWC_PRESENT ) lim.features |=BLK_FEAT_WRITE_CACHE |BLK_FEAT_FUA ;
Signature · 1 line
2.53.0