nvme fixes for Linux 6.16

- reset delayed remove_work after reconnect (Keith Busch)
  - fix atomic write size validation (Christoph Hellwig)
 -----BEGIN PGP SIGNATURE-----
 
 iQI/BAABCgApFiEEgdbnc3r/njty3Iq9D55TZVIEUYMFAmhdKXILHGhjaEBsc3Qu
 ZGUACgkQD55TZVIEUYOh2A/8CQabNBYW7bOAv7e/6Dx/mb+Rwu65DTyL1pztIsmK
 Apd8eo987J3GPVh2MI9FRQ9Z75zPnKUGTX2LFMVMqlk3X8AgAR24n+tAQSfciAUM
 fReqG6RTSkNGRMFbWNsNv6ZTx7Ihw2Q3+yI9BOmmLckn+EG9XEhviW6SvOj2zPfX
 bFh6z0QnyOUXkaT8h75PaV+9vIfJG85AvQsjIOoV9WToZRd1QHL9bs278gc7ZM7+
 EeCHY+zZcnXtmdv3O8u93rRSzmDXIDJOD2v3WPkHqoi2cD6F502+qcxX53vSLuWC
 Wpl0Wqmrnr9UtrUoxzhc1NUei+u3qdKOBtVgI34br3+W4s7PUx9qZ+3Ai6FjfY4b
 bhllhdEV6RND0k79Zu45hZGHsbUISyWDemgoJoAPD7qtUjOfxDXT0+fBOgobqcWE
 /rnJ3GQpKG1H18KsgU7VWBcZa4T+OCqQYQCUKeDvRUzsHu6y7hCb5xDAbwP2kME1
 ilbstckgu2RSBW4iGNZiSWk4oMruGF+t51E8iMvKumV5UMHmzAZP67mPbzBehPTM
 Zt6l0j8KfKZJ3ibteMHOkI8JSVnq2I35Rt1mrxsB4YTGsUwIS0sceTTAbsJ85C2Y
 dqcwb3jQa99aIlMnjsnAsFgqsr/rLqAHr79nGfHXrGPqlMrQBauNVJT0fkCSoJr0
 y9Q=
 =WezC
 -----END PGP SIGNATURE-----

Merge tag 'nvme-6.16-2025-06-26' of git://git.infradead.org/nvme into block-6.16

Pull NVMe fixes from Christoph:

" - reset delayed remove_work after reconnect (Keith Busch)
  - fix atomic write size validation (Christoph Hellwig)"

* tag 'nvme-6.16-2025-06-26' of git://git.infradead.org/nvme:
  nvme: fix atomic write size validation
  nvme: refactor the atomic write unit detection
  nvme: reset delayed remove_work after reconnect
This commit is contained in:
Jens Axboe 2025-06-26 07:31:52 -06:00
commit 5990b776fb
3 changed files with 44 additions and 48 deletions

View File

@ -2015,21 +2015,41 @@ 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->subsys->awupf) * 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);
lim->features |= BLK_FEAT_ATOMIC_WRITES;
return atomic_bs;
}
static u32 nvme_max_drv_segments(struct nvme_ctrl *ctrl)
@ -2067,34 +2087,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 */
@ -2382,16 +2376,6 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
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)
@ -3215,6 +3199,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 ||
@ -3552,6 +3537,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));
@ -3647,7 +3641,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;
@ -4036,6 +4029,10 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info)
list_add_tail_rcu(&ns->siblings, &head->list);
ns->head = head;
mutex_unlock(&ctrl->subsys->lock);
#ifdef CONFIG_NVME_MULTIPATH
cancel_delayed_work(&head->remove_work);
#endif
return 0;
out_put_ns_head:

View File

@ -1311,7 +1311,7 @@ void nvme_mpath_remove_disk(struct nvme_ns_head *head)
*/
if (!try_module_get(THIS_MODULE))
goto out;
queue_delayed_work(nvme_wq, &head->remove_work,
mod_delayed_work(nvme_wq, &head->remove_work,
head->delayed_removal_secs * HZ);
} else {
list_del_init(&head->entry);

View File

@ -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;
};
/*