All of lore.kernel.org
 help / color / mirror / Atom feed
From: Christoph Hellwig <hch@lst.de>
To: Keith Busch <kbusch@meta.com>
Cc: linux-nvme@lists.infradead.org, axboe@kernel.dk, hch@lst.de,
	Chris Mason <mason@kernel.org>, Sagi Grimberg <sagi@grimberg.me>,
	Chaitanya Kulkarni <kch@nvidia.com>,
	Keith Busch <kbusch@kernel.org>,
	Guixin Liu <kanie@linux.alibaba.com>
Subject: Re: [PATCH 19/19] nvmet: wait only for executing I/O in preempt and abort
Date: Tue, 3 Nov 2026 07:15:44 +0100	[thread overview]
Message-ID: <20261103061544.GM1862@lst.de> (raw)
In-Reply-To: <20261029155252.2332266-20-kbusch@meta.com>

Adding Guixin for the nvmet PR code.  It would have been useful
to group things a bit more..

On Thu, Oct 29, 2026 at 08:52:52AM -0700, Keith Busch wrote:
> From: Chris Mason <mason@kernel.org>
> 
> nvmet_parse_io_cmd() takes the pc_ref for an I/O command as soon as it's
> parsed, and "preempt and abort" waits for all pc_refs of the preempted
> controllers before it releases pr_sem. But a write that still needs its
> data can't complete without the host sending it and without the
> transport context receiving it, and that same context is where Register,
> Acquire and Release sleep on pr_sem. So a host that sends a write and
> holds the data back, then preempt and abort with its own key, then
> Register on the same queue, deadlocks io_work, the abort work and the
> write with each other. Two well-behaved hosts get there by accident
> when one fences the other while it re-registers under I/O. The
> connection can't be torn down, the namespace can't be disabled, and
> with nvmet_config_sem held across that, nobody can connect.
> 
> Take the pc_ref when the transport calls ->execute(), once all the data
> is there: nvmet_parse_io_cmd() stashes the real execute method and
> installs nvmet_pr_execute_io(), which gets the reference, rechecks the
> reservation access since the host may have lost it since parse time,
> and calls through. Preempt and abort then only waits for commands that
> have started executing, which need neither the host nor the transport
> to finish. A write whose data arrives after the abort now fails with
> Reservation Conflict where it was previously executed, that's the
> input the hang needs.
> 
> Fixes: 5a47c2080a73 ("nvmet: support reservation feature")
> Cc: Christoph Hellwig <hch@lst.de>
> Cc: Sagi Grimberg <sagi@grimberg.me>
> Cc: Chaitanya Kulkarni <kch@nvidia.com>
> Signed-off-by: Chris Mason <mason@kernel.org>
> Signed-off-by: Jens Axboe <axboe@kernel.dk>
> Signed-off-by: Keith Busch <kbusch@kernel.org>
> ---
>  drivers/nvme/target/core.c  |  2 +-
>  drivers/nvme/target/nvmet.h |  4 +++-
>  drivers/nvme/target/pr.c    | 42 ++++++++++++++++++++++++++++++++++++-
>  3 files changed, 45 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
> index 1ed250cd4b56e..4f6fb7c4181ab 100644
> --- a/drivers/nvme/target/core.c
> +++ b/drivers/nvme/target/core.c
> @@ -1176,7 +1176,7 @@ static u16 nvmet_parse_io_cmd(struct nvmet_req *req)
>  		if (ret)
>  			return ret;
>  
> -		ret = nvmet_pr_get_ns_pc_ref(req);
> +		nvmet_pr_set_execute(req);
>  	}
>  	return ret;
>  }
> diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
> index 7b429cc16d491..99b3bf5cbd3b6 100644
> --- a/drivers/nvme/target/nvmet.h
> +++ b/drivers/nvme/target/nvmet.h
> @@ -513,6 +513,8 @@ struct nvmet_req {
>  	struct nvmet_port	*port;
>  
>  	void (*execute)(struct nvmet_req *req);
> +	/* what nvmet_pr_execute_io() calls once it has the pc_ref */
> +	void (*pr_execute)(struct nvmet_req *req);
>  	const struct nvmet_fabrics_ops *ops;
>  
>  	struct pci_dev		*p2p_dev;
> @@ -1041,7 +1043,7 @@ void nvmet_pr_exit_ns(struct nvmet_ns *ns);
>  void nvmet_execute_get_log_page_resv(struct nvmet_req *req);
>  u16 nvmet_set_feat_resv_notif_mask(struct nvmet_req *req, u32 mask);
>  u16 nvmet_get_feat_resv_notif_mask(struct nvmet_req *req);
> -u16 nvmet_pr_get_ns_pc_ref(struct nvmet_req *req);
> +void nvmet_pr_set_execute(struct nvmet_req *req);
>  static inline void nvmet_pr_put_ns_pc_ref(struct nvmet_pr_per_ctrl_ref *pc_ref)
>  {
>  	percpu_ref_put(&pc_ref->ref);
> diff --git a/drivers/nvme/target/pr.c b/drivers/nvme/target/pr.c
> index 1a03f74c2e14f..798f5ce9011f8 100644
> --- a/drivers/nvme/target/pr.c
> +++ b/drivers/nvme/target/pr.c
> @@ -1060,7 +1060,7 @@ u16 nvmet_pr_check_cmd_access(struct nvmet_req *req)
>  	return status;
>  }
>  
> -u16 nvmet_pr_get_ns_pc_ref(struct nvmet_req *req)
> +static u16 nvmet_pr_get_ns_pc_ref(struct nvmet_req *req)
>  {
>  	struct nvmet_pr_per_ctrl_ref *pc_ref;
>  
> @@ -1072,6 +1072,46 @@ u16 nvmet_pr_get_ns_pc_ref(struct nvmet_req *req)
>  	return NVME_SC_SUCCESS;
>  }
>  
> +static void nvmet_pr_execute_io(struct nvmet_req *req)
> +{
> +	u16 status;
> +
> +	status = nvmet_pr_get_ns_pc_ref(req);
> +	if (!status) {
> +		/*
> +		 * The host can have lost its registration since the command was
> +		 * parsed. A get that succeeds after "preempt and abort" has
> +		 * resurrected the pc_ref has to see that. In percpu mode the
> +		 * get orders no later load, so this pairs with the
> +		 * smp_store_release() in __percpu_ref_switch_to_percpu(). While
> +		 * the resurrect is under way the get is a fully ordered
> +		 * atomic_long_inc_not_zero(). queue_work() in
> +		 * nvmet_execute_pr_acquire() orders the unregistration before
> +		 * the resurrect.
> +		 */
> +		smp_rmb();
> +		status = nvmet_pr_check_cmd_access(req);
> +	}
> +	if (status) {
> +		nvmet_req_complete(req, status);
> +		return;
> +	}
> +
> +	req->pr_execute(req);
> +}
> +
> +/*
> + * "Preempt and abort" waits for the commands that hold a pc_ref. Until the
> + * transport calls ->execute(), the end of a command can depend on the host,
> + * which may still have to send the data, and on the context and the resources
> + * of the transport. So a command gets its pc_ref only then.
> + */
> +void nvmet_pr_set_execute(struct nvmet_req *req)
> +{
> +	req->pr_execute = req->execute;
> +	req->execute = nvmet_pr_execute_io;
> +}
> +
>  static void nvmet_pr_ctrl_ns_all_cmds_done(struct percpu_ref *ref)
>  {
>  	struct nvmet_pr_per_ctrl_ref *pc_ref =
> -- 
> 2.52.0
---end quoted text---


  reply	other threads:[~2026-11-03  6:15 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-29 15:52 [PATCH 00/19] nvme patch pile Keith Busch
2026-10-29 15:52 ` [PATCH 01/19] nvme-loop: delete the controller if a reset fails Keith Busch
2026-11-03  5:50   ` Christoph Hellwig
2026-10-29 15:52 ` [PATCH 02/19] nvmet-tcp: ask for the TLS record type on every receive Keith Busch
2026-10-29 15:52 ` [PATCH 03/19] nvme-fabrics: reconnect if the association is gone Keith Busch
2026-11-03  5:58   ` Christoph Hellwig
2026-10-29 15:52 ` [PATCH 04/19] nvmet-auth: keep the old keys if the new ones fail Keith Busch
2026-11-03  5:59   ` Christoph Hellwig
2026-10-29 15:52 ` [PATCH 05/19] nvmet: accept I/O queues on the controller's port only Keith Busch
2026-11-03  6:00   ` Christoph Hellwig
2026-10-29 15:52 ` [PATCH 06/19] nvmet: check the host NQN against the TLS PSK identity Keith Busch
2026-11-03  6:01   ` Christoph Hellwig
2026-10-29 15:52 ` [PATCH 07/19] nvmet-tcp: let the accept work come to an end Keith Busch
2026-11-03  6:02   ` Christoph Hellwig
2026-10-29 15:52 ` [PATCH 08/19] nvme-tcp: start error recovery when a send fails Keith Busch
2026-10-29 16:17   ` Maurizio Lombardi
2026-10-29 15:52 ` [PATCH 09/19] nvmet: fix pc_ref use-after-free in preempt and abort Keith Busch
2026-11-03  6:04   ` Christoph Hellwig
2026-10-29 15:52 ` [PATCH 10/19] nvmet: move preempt and abort work off nvmet-wq Keith Busch
2026-10-29 15:52 ` [PATCH 11/19] nvmet-rdma: treat a failed connect as a disconnect Keith Busch
2026-11-03  6:06   ` Christoph Hellwig
2026-10-29 15:52 ` [PATCH 12/19] nvmet-rdma: disconnect only established queues Keith Busch
2026-11-03  6:06   ` Christoph Hellwig
2026-10-29 15:52 ` [PATCH 13/19] nvmet-rdma: take a device off the list last Keith Busch
2026-11-03  6:09   ` Christoph Hellwig
2026-10-29 15:52 ` [PATCH 14/19] nvmet-rdma: queue the release work under the mutex Keith Busch
2026-11-03  6:10   ` Christoph Hellwig
2026-10-29 15:52 ` [PATCH 15/19] nvmet-rdma: disconnect a queue on device removal Keith Busch
2026-11-03  6:12   ` Christoph Hellwig
2026-10-29 15:52 ` [PATCH 16/19] nvmet-rdma: reject queues larger than max_cqe Keith Busch
2026-11-03  6:14   ` Christoph Hellwig
2026-10-29 15:52 ` [PATCH 17/19] nvmet-rdma: don't run the wait list recursively Keith Busch
2026-10-29 15:52 ` [PATCH 18/19] nvmet-rdma: fix a lost run of the wait list Keith Busch
2026-10-29 15:52 ` [PATCH 19/19] nvmet: wait only for executing I/O in preempt and abort Keith Busch
2026-11-03  6:15   ` Christoph Hellwig [this message]
2026-11-04 20:37 ` (subset) [PATCH 00/19] nvme patch pile Keith Busch

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261103061544.GM1862@lst.de \
    --to=hch@lst.de \
    --cc=axboe@kernel.dk \
    --cc=kanie@linux.alibaba.com \
    --cc=kbusch@kernel.org \
    --cc=kbusch@meta.com \
    --cc=kch@nvidia.com \
    --cc=linux-nvme@lists.infradead.org \
    --cc=mason@kernel.org \
    --cc=sagi@grimberg.me \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.