mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/2] nvme: delete controllers with many namespaces faster
@ 2026-11-02  6:14 Qiliang Yuan
  2026-11-02  6:14 ` [PATCH 1/2] nvme: remove namespaces in parallel Qiliang Yuan
  2026-11-02  6:14 ` [PATCH 2/2] block: set QUEUE_FLAG_NO_ELV_SWITCH under the read lock in del_gendisk() Qiliang Yuan
  0 siblings, 2 replies; 6+ messages in thread
From: Qiliang Yuan @ 2026-11-02  6:14 UTC (permalink / raw)
  To: Keith Busch, Jens Axboe, Christoph Hellwig, Sagi Grimberg
  Cc: linux-nvme, linux-kernel, linux-block, Ming Lei, Nilay Shroff,
	Shin'ichiro Kawasaki, Stuart Hayes, Qiliang Yuan

Deleting an nvme controller removes its namespaces one after another,
about 20 ms each, so disconnecting from a fabrics target with 64
namespaces takes over a second.

Patch 1 removes the namespaces in parallel, as scanning already adds
them. Patch 2 drops the write lock that del_gendisk() takes on the tag
set, which would otherwise serialize the parallel removals again.

Disconnecting from a tcp target on the same host with 64 namespaces
drops from 1201-1308 ms to 115-124 ms. Details are in each patch.

Signed-off-by: Qiliang Yuan <odys.yuan@gmail.com>
---
Qiliang Yuan (2):
      nvme: remove namespaces in parallel
      block: set QUEUE_FLAG_NO_ELV_SWITCH under the read lock in del_gendisk()

 block/genhd.c            | 17 +++++------------
 drivers/nvme/host/core.c | 13 ++++++++++++-
 2 files changed, 17 insertions(+), 13 deletions(-)
---
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
change-id: 20261102-bug-nvme-remove-ns-async-ceaaa1eda6a7

Best regards,
-- 
Qiliang Yuan <odys.yuan@gmail.com>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/2] nvme: remove namespaces in parallel
  2026-11-02  6:14 [PATCH 0/2] nvme: delete controllers with many namespaces faster Qiliang Yuan
@ 2026-11-02  6:14 ` Qiliang Yuan
  2026-11-02  7:26   ` Hannes Reinecke
  2026-11-02  6:14 ` [PATCH 2/2] block: set QUEUE_FLAG_NO_ELV_SWITCH under the read lock in del_gendisk() Qiliang Yuan
  1 sibling, 1 reply; 6+ messages in thread
From: Qiliang Yuan @ 2026-11-02  6:14 UTC (permalink / raw)
  To: Keith Busch, Jens Axboe, Christoph Hellwig, Sagi Grimberg
  Cc: linux-nvme, linux-kernel, linux-block, Ming Lei, Nilay Shroff,
	Shin'ichiro Kawasaki, Stuart Hayes, Qiliang Yuan

nvme_remove_namespaces() removes the namespaces of a controller one
after another. Each removal waits for RCU grace periods in
del_gendisk() for the namespace and for its multipath head, about
20 ms in all.

Deleting a controller, such as disconnecting from a fabrics target,
thus takes time proportional to its namespace count, over a second for
64 namespaces. Scanning had the same problem until commit 4e893ca81170
("nvme_core: scan namespaces asynchronously") made it add namespaces
in parallel.

Remove the namespaces through an async domain the same way, and wait
for all of them before returning.

Disconnect time from a target on the same host, over tcp to 127.0.0.1
and over nvme-loop, with 4 I/O queues, median of 3 runs on each of two
boots per kernel:

  namespaces  transport  before              after
           8  tcp         170 /  171 ms        85 /   91 ms
          32  tcp         628 /  628 ms       294 /  293 ms
          64  tcp        1201 / 1308 ms       533 /  550 ms
          64  loop       1988 / 1913 ms      1283 / 1200 ms

Signed-off-by: Qiliang Yuan <odys.yuan@gmail.com>
---
 drivers/nvme/host/core.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index a7a4d09644c13..18948c2ba218d 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -4716,6 +4716,11 @@ static void nvme_scan_work(struct work_struct *work)
 #endif
 }
 
+static void nvme_ns_remove_async(void *data, async_cookie_t cookie)
+{
+	nvme_ns_remove(data);
+}
+
 /*
  * This function iterates the namespace list unlocked to allow recovery from
  * controller failure. It is up to the caller to ensure the namespace list is
@@ -4725,6 +4730,7 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
 {
 	struct nvme_ns *ns, *next;
 	LIST_HEAD(ns_list);
+	ASYNC_DOMAIN(domain);
 
 	/*
 	 * make sure to requeue I/O to all namespaces as these
@@ -4759,8 +4765,13 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
 	mutex_unlock(&ctrl->namespaces_lock);
 	synchronize_srcu(&ctrl->srcu);
 
+	/*
+	 * Remove the namespaces in parallel, as each removal waits for several
+	 * RCU grace periods.
+	 */
 	list_for_each_entry_safe(ns, next, &ns_list, list)
-		nvme_ns_remove(ns);
+		async_schedule_domain(nvme_ns_remove_async, ns, &domain);
+	async_synchronize_full_domain(&domain);
 }
 EXPORT_SYMBOL_GPL(nvme_remove_namespaces);
 

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/2] block: set QUEUE_FLAG_NO_ELV_SWITCH under the read lock in del_gendisk()
  2026-11-02  6:14 [PATCH 0/2] nvme: delete controllers with many namespaces faster Qiliang Yuan
  2026-11-02  6:14 ` [PATCH 1/2] nvme: remove namespaces in parallel Qiliang Yuan
@ 2026-11-02  6:14 ` Qiliang Yuan
  2026-11-02  7:22   ` Hannes Reinecke
  1 sibling, 1 reply; 6+ messages in thread
From: Qiliang Yuan @ 2026-11-02  6:14 UTC (permalink / raw)
  To: Keith Busch, Jens Axboe, Christoph Hellwig, Sagi Grimberg
  Cc: linux-nvme, linux-kernel, linux-block, Ming Lei, Nilay Shroff,
	Shin'ichiro Kawasaki, Stuart Hayes, Qiliang Yuan

del_gendisk() takes the tag set's update_nr_hwq_lock for write to set
QUEUE_FLAG_NO_ELV_SWITCH, then for read around __del_gendisk(). The
write lock was there to wait for elevator switches from sysfs, which
held the lock for read when the flag was added.

When several disks of one tag set are deleted at once, as nvme does for
the namespaces of a controller, each write lock waits for the other
disks to get through __del_gendisk() under the read lock, and the
deletions run one after another.

Elevator switches from sysfs have taken update_nr_hwq_lock for write
since commit 4ff58d6bc9dd4 ("block: serialize elevator changes for the
same queue using a writer lock"), as updates of nr_hw_queues do. Set the
flag under the read lock instead, which still waits for both and keeps
them out.

Disconnect time of an nvme controller that removes its namespaces in
parallel, from a target on the same host, over tcp to 127.0.0.1 and
over nvme-loop, with 4 I/O queues, median of 3 runs on each of two
boots per kernel:

  namespaces  transport  before              after
           8  tcp          85 /   91 ms        51 /   47 ms
          32  tcp         294 /  293 ms        75 /   70 ms
          64  tcp         533 /  550 ms       115 /  124 ms
          64  loop       1283 / 1200 ms       185 /  204 ms

Signed-off-by: Qiliang Yuan <odys.yuan@gmail.com>
---
 block/genhd.c | 17 +++++------------
 1 file changed, 5 insertions(+), 12 deletions(-)

diff --git a/block/genhd.c b/block/genhd.c
index 10ca8b4d6eea7..5afc60bf139e6 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -781,16 +781,6 @@ static void __del_gendisk(struct gendisk *disk)
 		blk_unfreeze_release_lock(q);
 }
 
-static void disable_elv_switch(struct request_queue *q)
-{
-	struct blk_mq_tag_set *set = q->tag_set;
-	WARN_ON_ONCE(!queue_is_mq(q));
-
-	down_write(&set->update_nr_hwq_lock);
-	blk_queue_flag_set(QUEUE_FLAG_NO_ELV_SWITCH, q);
-	up_write(&set->update_nr_hwq_lock);
-}
-
 /**
  * del_gendisk - remove the gendisk
  * @disk: the struct gendisk to remove
@@ -820,10 +810,13 @@ void del_gendisk(struct gendisk *disk)
 	} else {
 		set = disk->queue->tag_set;
 
-		disable_elv_switch(disk->queue);
-
 		memflags = memalloc_noio_save();
 		down_read(&set->update_nr_hwq_lock);
+		/*
+		 * Set the flag under the read lock, as elevator switches take
+		 * update_nr_hwq_lock for write.
+		 */
+		blk_queue_flag_set(QUEUE_FLAG_NO_ELV_SWITCH, disk->queue);
 		__del_gendisk(disk);
 		up_read(&set->update_nr_hwq_lock);
 		memalloc_noio_restore(memflags);

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] block: set QUEUE_FLAG_NO_ELV_SWITCH under the read lock in del_gendisk()
  2026-11-02  6:14 ` [PATCH 2/2] block: set QUEUE_FLAG_NO_ELV_SWITCH under the read lock in del_gendisk() Qiliang Yuan
@ 2026-11-02  7:22   ` Hannes Reinecke
  0 siblings, 0 replies; 6+ messages in thread
From: Hannes Reinecke @ 2026-11-02  7:22 UTC (permalink / raw)
  To: Qiliang Yuan, Keith Busch, Jens Axboe, Christoph Hellwig, Sagi Grimberg
  Cc: linux-nvme, linux-kernel, linux-block, Ming Lei, Nilay Shroff,
	Shin'ichiro Kawasaki, Stuart Hayes

On 11/2/26 7:14 AM, Qiliang Yuan wrote:
> del_gendisk() takes the tag set's update_nr_hwq_lock for write to set
> QUEUE_FLAG_NO_ELV_SWITCH, then for read around __del_gendisk(). The
> write lock was there to wait for elevator switches from sysfs, which
> held the lock for read when the flag was added.
> 
> When several disks of one tag set are deleted at once, as nvme does for
> the namespaces of a controller, each write lock waits for the other
> disks to get through __del_gendisk() under the read lock, and the
> deletions run one after another.
> 
> Elevator switches from sysfs have taken update_nr_hwq_lock for write
> since commit 4ff58d6bc9dd4 ("block: serialize elevator changes for the
> same queue using a writer lock"), as updates of nr_hw_queues do. Set the
> flag under the read lock instead, which still waits for both and keeps
> them out.
> 
> Disconnect time of an nvme controller that removes its namespaces in
> parallel, from a target on the same host, over tcp to 127.0.0.1 and
> over nvme-loop, with 4 I/O queues, median of 3 runs on each of two
> boots per kernel:
> 
>    namespaces  transport  before              after
>             8  tcp          85 /   91 ms        51 /   47 ms
>            32  tcp         294 /  293 ms        75 /   70 ms
>            64  tcp         533 /  550 ms       115 /  124 ms
>            64  loop       1283 / 1200 ms       185 /  204 ms
> 
> Signed-off-by: Qiliang Yuan <odys.yuan@gmail.com>
> ---
>   block/genhd.c | 17 +++++------------
>   1 file changed, 5 insertions(+), 12 deletions(-)
> 
> diff --git a/block/genhd.c b/block/genhd.c
> index 10ca8b4d6eea7..5afc60bf139e6 100644
> --- a/block/genhd.c
> +++ b/block/genhd.c
> @@ -781,16 +781,6 @@ static void __del_gendisk(struct gendisk *disk)
>   		blk_unfreeze_release_lock(q);
>   }
>   
> -static void disable_elv_switch(struct request_queue *q)
> -{
> -	struct blk_mq_tag_set *set = q->tag_set;
> -	WARN_ON_ONCE(!queue_is_mq(q));
> -
> -	down_write(&set->update_nr_hwq_lock);
> -	blk_queue_flag_set(QUEUE_FLAG_NO_ELV_SWITCH, q);
> -	up_write(&set->update_nr_hwq_lock);
> -}
> -
>   /**
>    * del_gendisk - remove the gendisk
>    * @disk: the struct gendisk to remove
> @@ -820,10 +810,13 @@ void del_gendisk(struct gendisk *disk)
>   	} else {
>   		set = disk->queue->tag_set;
>   
> -		disable_elv_switch(disk->queue);
> -
>   		memflags = memalloc_noio_save();
>   		down_read(&set->update_nr_hwq_lock);
> +		/*
> +		 * Set the flag under the read lock, as elevator switches take
> +		 * update_nr_hwq_lock for write.
> +		 */
> +		blk_queue_flag_set(QUEUE_FLAG_NO_ELV_SWITCH, disk->queue);
>   		__del_gendisk(disk);
>   		up_read(&set->update_nr_hwq_lock);
>   		memalloc_noio_restore(memflags);
> 

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 1/2] nvme: remove namespaces in parallel
  2026-11-02  6:14 ` [PATCH 1/2] nvme: remove namespaces in parallel Qiliang Yuan
@ 2026-11-02  7:26   ` Hannes Reinecke
  2026-11-02  8:00     ` Qiliang Yuan
  0 siblings, 1 reply; 6+ messages in thread
From: Hannes Reinecke @ 2026-11-02  7:26 UTC (permalink / raw)
  To: Qiliang Yuan, Keith Busch, Jens Axboe, Christoph Hellwig, Sagi Grimberg
  Cc: linux-nvme, linux-kernel, linux-block, Ming Lei, Nilay Shroff,
	Shin'ichiro Kawasaki, Stuart Hayes

On 11/2/26 7:14 AM, Qiliang Yuan wrote:
> nvme_remove_namespaces() removes the namespaces of a controller one
> after another. Each removal waits for RCU grace periods in
> del_gendisk() for the namespace and for its multipath head, about
> 20 ms in all.
> 
But that isn't changed by calling async_schedule_domain, isn't it?

> Deleting a controller, such as disconnecting from a fabrics target,
> thus takes time proportional to its namespace count, over a second for
> 64 namespaces. Scanning had the same problem until commit 4e893ca81170
> ("nvme_core: scan namespaces asynchronously") made it add namespaces
> in parallel.
> 
> Remove the namespaces through an async domain the same way, and wait
> for all of them before returning.
> 
> Disconnect time from a target on the same host, over tcp to 127.0.0.1
> and over nvme-loop, with 4 I/O queues, median of 3 runs on each of two
> boots per kernel:
> 
>    namespaces  transport  before              after
>             8  tcp         170 /  171 ms        85 /   91 ms
>            32  tcp         628 /  628 ms       294 /  293 ms
>            64  tcp        1201 / 1308 ms       533 /  550 ms
>            64  loop       1988 / 1913 ms      1283 / 1200 ms
> 
> Signed-off-by: Qiliang Yuan <odys.yuan@gmail.com>
> ---
>   drivers/nvme/host/core.c | 13 ++++++++++++-
>   1 file changed, 12 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
> index a7a4d09644c13..18948c2ba218d 100644
> --- a/drivers/nvme/host/core.c
> +++ b/drivers/nvme/host/core.c
> @@ -4716,6 +4716,11 @@ static void nvme_scan_work(struct work_struct *work)
>   #endif
>   }
>   
> +static void nvme_ns_remove_async(void *data, async_cookie_t cookie)
> +{
> +	nvme_ns_remove(data);
> +}
> +
>   /*
>    * This function iterates the namespace list unlocked to allow recovery from
>    * controller failure. It is up to the caller to ensure the namespace list is
> @@ -4725,6 +4730,7 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
>   {
>   	struct nvme_ns *ns, *next;
>   	LIST_HEAD(ns_list);
> +	ASYNC_DOMAIN(domain);
>   
>   	/*
>   	 * make sure to requeue I/O to all namespaces as these
> @@ -4759,8 +4765,13 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
>   	mutex_unlock(&ctrl->namespaces_lock);
>   	synchronize_srcu(&ctrl->srcu);
>   
> +	/*
> +	 * Remove the namespaces in parallel, as each removal waits for several
> +	 * RCU grace periods.
> +	 */
>   	list_for_each_entry_safe(ns, next, &ns_list, list)
> -		nvme_ns_remove(ns);
> +		async_schedule_domain(nvme_ns_remove_async, ns, &domain);
> +	async_synchronize_full_domain(&domain);
>   }
>   EXPORT_SYMBOL_GPL(nvme_remove_namespaces);
>   
> 
Other than that it looks fine.

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 1/2] nvme: remove namespaces in parallel
  2026-11-02  7:26   ` Hannes Reinecke
@ 2026-11-02  8:00     ` Qiliang Yuan
  0 siblings, 0 replies; 6+ messages in thread
From: Qiliang Yuan @ 2026-11-02  8:00 UTC (permalink / raw)
  To: Hannes Reinecke
  Cc: Keith Busch, Jens Axboe, Christoph Hellwig, Sagi Grimberg,
	linux-nvme, linux-kernel, linux-block, Ming Lei, Nilay Shroff,
	Shin'ichiro Kawasaki, Stuart Hayes

On Mon, 2 Nov 2026 08:26:57 +0100, Hannes Reinecke wrote:
> On 11/2/26 7:14 AM, Qiliang Yuan wrote:
>> nvme_remove_namespaces() removes the namespaces of a controller one
>> after another. Each removal waits for RCU grace periods in
>> del_gendisk() for the namespace and for its multipath head, about
>> 20 ms in all.
>>
> But that isn't changed by calling async_schedule_domain, isn't it?

Right, each removal still waits about 20 ms. What changes is that the
waits overlap. A grace period is system-wide, so removals that wait at
the same time are released by the same grace periods instead of each
needing its own. Disconnect time over tcp, from the table in the
patch:

  namespaces  before              after
           8   170 /  171 ms       85 /  91 ms
          32   628 /  628 ms      294 / 293 ms
          64  1201 / 1308 ms      533 / 550 ms

> Other than that it looks fine.

Thanks for the review.

Qiliang

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-11-02  8:00 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-02  6:14 [PATCH 0/2] nvme: delete controllers with many namespaces faster Qiliang Yuan
2026-11-02  6:14 ` [PATCH 1/2] nvme: remove namespaces in parallel Qiliang Yuan
2026-11-02  7:26   ` Hannes Reinecke
2026-11-02  8:00     ` Qiliang Yuan
2026-11-02  6:14 ` [PATCH 2/2] block: set QUEUE_FLAG_NO_ELV_SWITCH under the read lock in del_gendisk() Qiliang Yuan
2026-11-02  7:22   ` Hannes Reinecke

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®