* [PATCH v2 0/2] nvme-multipath: requeue work fixes
@ 2026-11-11 13:10 Qiliang Yuan
2026-11-11 13:10 ` [PATCH v2 1/2] nvme-multipath: fail queued I/O when delayed_removal_secs is cleared Qiliang Yuan
2026-11-11 13:10 ` [PATCH v2 2/2] nvme-multipath: don't wait for a grace period in nvme_mpath_set_live() Qiliang Yuan
0 siblings, 2 replies; 6+ messages in thread
From: Qiliang Yuan @ 2026-11-11 13:10 UTC (permalink / raw)
To: Keith Busch, Jens Axboe, Christoph Hellwig, Sagi Grimberg,
Nilay Shroff, Hannes Reinecke
Cc: linux-nvme, linux-kernel, Qiliang Yuan
Patch 1 fails I/O queued without a path as soon as delayed_removal_secs
is cleared, instead of when the delayed removal runs.
Patch 2 stops nvme_mpath_set_live() from waiting for an SRCU grace
period per namespace, which makes an ANA change with many namespaces
take tens to hundreds of milliseconds.
Signed-off-by: Qiliang Yuan <qiliang.yuan@linux.dev>
---
V1 -> V2:
- Add patch 1
- Take the SRCU cookie under head->lock in patch 2 (Sashiko)
- Rerun the tests
v1: https://lore.kernel.org/r/20261110-bug-nvme-mpath-set-live-srcu-v1-1-1f58b405b0f8@linux.dev
---
Qiliang Yuan (2):
nvme-multipath: fail queued I/O when delayed_removal_secs is cleared
nvme-multipath: don't wait for a grace period in nvme_mpath_set_live()
drivers/nvme/host/multipath.c | 19 ++++++++++++++++++-
drivers/nvme/host/nvme.h | 1 +
2 files changed, 19 insertions(+), 1 deletion(-)
---
base-commit: b3dfbde62cf1981c4638939c159dfd087b7a72e9
change-id: 20261110-bug-nvme-mpath-set-live-srcu-cbd8965f1457
Best regards,
--
Qiliang Yuan <qiliang.yuan@linux.dev>
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v2 1/2] nvme-multipath: fail queued I/O when delayed_removal_secs is cleared 2026-11-11 13:10 [PATCH v2 0/2] nvme-multipath: requeue work fixes Qiliang Yuan @ 2026-11-11 13:10 ` Qiliang Yuan 2026-11-12 16:46 ` Nilay Shroff 2026-11-11 13:10 ` [PATCH v2 2/2] nvme-multipath: don't wait for a grace period in nvme_mpath_set_live() Qiliang Yuan 1 sibling, 1 reply; 6+ messages in thread From: Qiliang Yuan @ 2026-11-11 13:10 UTC (permalink / raw) To: Keith Busch, Jens Axboe, Christoph Hellwig, Sagi Grimberg, Nilay Shroff, Hannes Reinecke Cc: linux-nvme, linux-kernel, Qiliang Yuan With delayed_removal_secs set, bios submitted after the last path goes away are queued on head->requeue_list, in case a path comes back before the delayed removal of the head. Writing 0 to delayed_removal_secs clears NVME_NSHEAD_QUEUE_IF_NO_PATH but doesn't kick the requeue work, so the bios already queued stay there until the removal work runs, up to the old delay later, instead of failing right away. Kick the requeue work when the attribute is cleared, as fail_on_ctrl_loss_store() does when it is set, so that the queued bios are resubmitted and fail. With delayed_removal_secs at 30 and no path left, a direct read is queued and delayed_removal_secs cleared two seconds later. The read then fails after (mean of two runs, min-max in parentheses): before (ms) after (ms) change 26234 (26209-26258) 3.5 (3-4) -99.99% Fixes: 62188639ec160 ("nvme-multipath: introduce delayed removal of the multipath head node") Signed-off-by: Qiliang Yuan <qiliang.yuan@linux.dev> --- drivers/nvme/host/multipath.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index ae8059366b57d..a92a05b1f925a 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -1249,6 +1249,10 @@ static ssize_t delayed_removal_secs_store(struct device *dev, */ synchronize_srcu(&head->srcu); + /* Fail the I/O queued while waiting for a path to come back */ + if (!sec) + kblockd_schedule_work(&head->requeue_work); + return count; } -- 2.43.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 1/2] nvme-multipath: fail queued I/O when delayed_removal_secs is cleared 2026-11-11 13:10 ` [PATCH v2 1/2] nvme-multipath: fail queued I/O when delayed_removal_secs is cleared Qiliang Yuan @ 2026-11-12 16:46 ` Nilay Shroff 0 siblings, 0 replies; 6+ messages in thread From: Nilay Shroff @ 2026-11-12 16:46 UTC (permalink / raw) To: Qiliang Yuan, Keith Busch, Jens Axboe, Christoph Hellwig, Sagi Grimberg, Hannes Reinecke Cc: linux-nvme, linux-kernel On 11/11/26 6:40 PM, Qiliang Yuan wrote: > With delayed_removal_secs set, bios submitted after the last path goes > away are queued on head->requeue_list, in case a path comes back before > the delayed removal of the head. > > Writing 0 to delayed_removal_secs clears NVME_NSHEAD_QUEUE_IF_NO_PATH > but doesn't kick the requeue work, so the bios already queued stay > there until the removal work runs, up to the old delay later, instead > of failing right away. > > Kick the requeue work when the attribute is cleared, as > fail_on_ctrl_loss_store() does when it is set, so that the queued bios > are resubmitted and fail. > > With delayed_removal_secs at 30 and no path left, a direct read is > queued and delayed_removal_secs cleared two seconds later. The read > then fails after (mean of two runs, min-max in parentheses): > > before (ms) after (ms) change > 26234 (26209-26258) 3.5 (3-4) -99.99% > > Fixes: 62188639ec160 ("nvme-multipath: introduce delayed removal of the multipath head node") > Signed-off-by: Qiliang Yuan <qiliang.yuan@linux.dev> > --- > drivers/nvme/host/multipath.c | 4 ++++ > 1 file changed, 4 insertions(+) > > diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c > index ae8059366b57d..a92a05b1f925a 100644 > --- a/drivers/nvme/host/multipath.c > +++ b/drivers/nvme/host/multipath.c > @@ -1249,6 +1249,10 @@ static ssize_t delayed_removal_secs_store(struct device *dev, > */ > synchronize_srcu(&head->srcu); > > + /* Fail the I/O queued while waiting for a path to come back */ > + if (!sec) > + kblockd_schedule_work(&head->requeue_work); > + > return count; > } > > I think the original design was to apply an updated delayed_removal_secs value to future namespace head removals, while allowing an already pending removal to proceed using the previously configured delay. When the delayed-removal timer expires, the removal work clears disk-live, waits for the SRCU grace period, and then schedules requeue_work to fail I/O that was queued while waiting for a path to return. Given this, I'm wondering why we only kick requeue_work when delayed_removal_secs is set to zero. If a delayed-removal work item is already pending, wouldn't it be more consistent to rearm that work based on the updated timeout, rather than only scheduling requeue_work? This would also ensure that changes to the configured delay are reflected in an already pending removal. Thanks, --Nilay ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 2/2] nvme-multipath: don't wait for a grace period in nvme_mpath_set_live() 2026-11-11 13:10 [PATCH v2 0/2] nvme-multipath: requeue work fixes Qiliang Yuan 2026-11-11 13:10 ` [PATCH v2 1/2] nvme-multipath: fail queued I/O when delayed_removal_secs is cleared Qiliang Yuan @ 2026-11-11 13:10 ` Qiliang Yuan 2026-11-12 15:28 ` Hannes Reinecke 2026-11-12 16:47 ` Nilay Shroff 1 sibling, 2 replies; 6+ messages in thread From: Qiliang Yuan @ 2026-11-11 13:10 UTC (permalink / raw) To: Keith Busch, Jens Axboe, Christoph Hellwig, Sagi Grimberg, Nilay Shroff, Hannes Reinecke Cc: linux-nvme, linux-kernel, Qiliang Yuan When a path goes live, nvme_mpath_set_live() waits for an SRCU grace period of the namespace head before kicking the requeue work, so that bios which found no usable path are on the requeue list by the time it runs. The ANA work calls nvme_mpath_set_live() for every namespace that becomes optimized or non-optimized, one after another, so an ANA change waits for one grace period per namespace. Start the grace period with start_poll_synchronize_srcu() instead and have nvme_requeue_work() wait for it only if it has not completed yet. Take the cookie under head->lock, so that concurrent callers store their cookies in order and the requeue work never sees an older one. The grace periods of all namespaces then run in parallel, and requeue work kicked for other reasons finds the cookie completed and does not wait. Flipping an ANA group from inaccessible to optimized ten times over nvme-tcp loopback, nvme_read_ana_log() takes (median, min-max in parentheses): namespaces before (ms) after (ms) change 256 51.9 (42.5-85.4) 8.9 (2.6-12.0) -83% 1024 157.5 (133.5-179.6) 49.5 (32.8-67.1) -69% Signed-off-by: Qiliang Yuan <qiliang.yuan@linux.dev> --- drivers/nvme/host/multipath.c | 15 ++++++++++++++- drivers/nvme/host/nvme.h | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index a92a05b1f925a..9c1023102902c 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -712,6 +712,11 @@ static void nvme_requeue_work(struct work_struct *work) container_of(work, struct nvme_ns_head, requeue_work); struct bio *bio, *next; + /* Let bios that found no usable path reach the requeue list first. */ + if (!poll_state_synchronize_srcu(&head->srcu, + READ_ONCE(head->requeue_srcu_cookie))) + synchronize_srcu(&head->srcu); + spin_lock_irq(&head->requeue_lock); next = bio_list_get(&head->requeue_list); spin_unlock_irq(&head->requeue_lock); @@ -767,6 +772,7 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head) mutex_init(&head->lock); spin_lock_init(&head->requeue_lock); INIT_WORK(&head->requeue_work, nvme_requeue_work); + head->requeue_srcu_cookie = get_completed_synchronize_srcu(); INIT_WORK(&head->partition_scan_work, nvme_partition_scan_work); INIT_DELAYED_WORK(&head->remove_work, nvme_remove_head_work); @@ -849,9 +855,16 @@ static void nvme_mpath_set_live(struct nvme_ns *ns) __nvme_find_path(head, node); srcu_read_unlock(&head->srcu, srcu_idx); } + /* + * Have nvme_requeue_work() wait for the grace period instead of + * blocking here, so that namespaces going live together, e.g. on + * an ANA change, do not wait for one grace period each. Start it + * under head->lock so that the newest cookie is stored last. + */ + WRITE_ONCE(head->requeue_srcu_cookie, + start_poll_synchronize_srcu(&head->srcu)); mutex_unlock(&head->lock); - synchronize_srcu(&head->srcu); nvme_mpath_revalidate_zones(head); kblockd_schedule_work(&head->requeue_work); } diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 8833a463f1c84..3fff9b394b1cd 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -587,6 +587,7 @@ struct nvme_ns_head { __guarded_by(&requeue_lock); spinlock_t requeue_lock; struct work_struct requeue_work; + unsigned long requeue_srcu_cookie; struct work_struct partition_scan_work; struct mutex lock; unsigned long flags; -- 2.43.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 2/2] nvme-multipath: don't wait for a grace period in nvme_mpath_set_live() 2026-11-11 13:10 ` [PATCH v2 2/2] nvme-multipath: don't wait for a grace period in nvme_mpath_set_live() Qiliang Yuan @ 2026-11-12 15:28 ` Hannes Reinecke 2026-11-12 16:47 ` Nilay Shroff 1 sibling, 0 replies; 6+ messages in thread From: Hannes Reinecke @ 2026-11-12 15:28 UTC (permalink / raw) To: Qiliang Yuan, Keith Busch, Jens Axboe, Christoph Hellwig, Sagi Grimberg, Nilay Shroff Cc: linux-nvme, linux-kernel On 11/11/26 2:10 PM, Qiliang Yuan wrote: > When a path goes live, nvme_mpath_set_live() waits for an SRCU grace > period of the namespace head before kicking the requeue work, so that > bios which found no usable path are on the requeue list by the time it > runs. > > The ANA work calls nvme_mpath_set_live() for every namespace that > becomes optimized or non-optimized, one after another, so an ANA change > waits for one grace period per namespace. > > Start the grace period with start_poll_synchronize_srcu() instead and > have nvme_requeue_work() wait for it only if it has not completed yet. > Take the cookie under head->lock, so that concurrent callers store > their cookies in order and the requeue work never sees an older one. > The grace periods of all namespaces then run in parallel, and requeue > work kicked for other reasons finds the cookie completed and does not > wait. > > Flipping an ANA group from inaccessible to optimized ten times over > nvme-tcp loopback, nvme_read_ana_log() takes (median, min-max in > parentheses): > > namespaces before (ms) after (ms) change > 256 51.9 (42.5-85.4) 8.9 (2.6-12.0) -83% > 1024 157.5 (133.5-179.6) 49.5 (32.8-67.1) -69% > > Signed-off-by: Qiliang Yuan <qiliang.yuan@linux.dev> > --- > drivers/nvme/host/multipath.c | 15 ++++++++++++++- > drivers/nvme/host/nvme.h | 1 + > 2 files changed, 15 insertions(+), 1 deletion(-) > > diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c > index a92a05b1f925a..9c1023102902c 100644 > --- a/drivers/nvme/host/multipath.c > +++ b/drivers/nvme/host/multipath.c > @@ -712,6 +712,11 @@ static void nvme_requeue_work(struct work_struct *work) > container_of(work, struct nvme_ns_head, requeue_work); > struct bio *bio, *next; > > + /* Let bios that found no usable path reach the requeue list first. */ > + if (!poll_state_synchronize_srcu(&head->srcu, > + READ_ONCE(head->requeue_srcu_cookie))) > + synchronize_srcu(&head->srcu); > + > spin_lock_irq(&head->requeue_lock); > next = bio_list_get(&head->requeue_list); > spin_unlock_irq(&head->requeue_lock); > @@ -767,6 +772,7 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head) > mutex_init(&head->lock); > spin_lock_init(&head->requeue_lock); > INIT_WORK(&head->requeue_work, nvme_requeue_work); > + head->requeue_srcu_cookie = get_completed_synchronize_srcu(); > INIT_WORK(&head->partition_scan_work, nvme_partition_scan_work); > INIT_DELAYED_WORK(&head->remove_work, nvme_remove_head_work); > > @@ -849,9 +855,16 @@ static void nvme_mpath_set_live(struct nvme_ns *ns) > __nvme_find_path(head, node); > srcu_read_unlock(&head->srcu, srcu_idx); > } > + /* > + * Have nvme_requeue_work() wait for the grace period instead of > + * blocking here, so that namespaces going live together, e.g. on > + * an ANA change, do not wait for one grace period each. Start it > + * under head->lock so that the newest cookie is stored last. > + */ > + WRITE_ONCE(head->requeue_srcu_cookie, > + start_poll_synchronize_srcu(&head->srcu)); > mutex_unlock(&head->lock); > > - synchronize_srcu(&head->srcu); > nvme_mpath_revalidate_zones(head); > kblockd_schedule_work(&head->requeue_work); > } > diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h > index 8833a463f1c84..3fff9b394b1cd 100644 > --- a/drivers/nvme/host/nvme.h > +++ b/drivers/nvme/host/nvme.h > @@ -587,6 +587,7 @@ struct nvme_ns_head { > __guarded_by(&requeue_lock); > spinlock_t requeue_lock; > struct work_struct requeue_work; > + unsigned long requeue_srcu_cookie; > struct work_struct partition_scan_work; > struct mutex lock; > unsigned long flags; > Looks good. Reviewed-by: Hannes Reinecke <hare@kernel.org> Cheers, Hannes -- Dr. Hannes Reinecke Kernel Storage Architect hare@suse.de +49 911 74053 688 SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 2/2] nvme-multipath: don't wait for a grace period in nvme_mpath_set_live() 2026-11-11 13:10 ` [PATCH v2 2/2] nvme-multipath: don't wait for a grace period in nvme_mpath_set_live() Qiliang Yuan 2026-11-12 15:28 ` Hannes Reinecke @ 2026-11-12 16:47 ` Nilay Shroff 1 sibling, 0 replies; 6+ messages in thread From: Nilay Shroff @ 2026-11-12 16:47 UTC (permalink / raw) To: Qiliang Yuan, Keith Busch, Jens Axboe, Christoph Hellwig, Sagi Grimberg, Hannes Reinecke Cc: linux-nvme, linux-kernel On 11/11/26 6:40 PM, Qiliang Yuan wrote: > When a path goes live, nvme_mpath_set_live() waits for an SRCU grace > period of the namespace head before kicking the requeue work, so that > bios which found no usable path are on the requeue list by the time it > runs. > > The ANA work calls nvme_mpath_set_live() for every namespace that > becomes optimized or non-optimized, one after another, so an ANA change > waits for one grace period per namespace. > > Start the grace period with start_poll_synchronize_srcu() instead and > have nvme_requeue_work() wait for it only if it has not completed yet. > Take the cookie under head->lock, so that concurrent callers store > their cookies in order and the requeue work never sees an older one. > The grace periods of all namespaces then run in parallel, and requeue > work kicked for other reasons finds the cookie completed and does not > wait. > > Flipping an ANA group from inaccessible to optimized ten times over > nvme-tcp loopback, nvme_read_ana_log() takes (median, min-max in > parentheses): > > namespaces before (ms) after (ms) change > 256 51.9 (42.5-85.4) 8.9 (2.6-12.0) -83% > 1024 157.5 (133.5-179.6) 49.5 (32.8-67.1) -69% > > Signed-off-by: Qiliang Yuan<qiliang.yuan@linux.dev> Looks good to me: Reviewed-by: Nilay Shroff <nilay@linux.ibm.com> ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-12 16:47 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-11-11 13:10 [PATCH v2 0/2] nvme-multipath: requeue work fixes Qiliang Yuan 2026-11-11 13:10 ` [PATCH v2 1/2] nvme-multipath: fail queued I/O when delayed_removal_secs is cleared Qiliang Yuan 2026-11-12 16:46 ` Nilay Shroff 2026-11-11 13:10 ` [PATCH v2 2/2] nvme-multipath: don't wait for a grace period in nvme_mpath_set_live() Qiliang Yuan 2026-11-12 15:28 ` Hannes Reinecke 2026-11-12 16:47 ` Nilay Shroff
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®