From: Mohamed Khalfella <mkhalfella@purestorage.com>
To: Keith Busch <kbusch@kernel.org>, Jens Axboe <axboe@kernel.dk>,
Christoph Hellwig <hch@lst.de>, Sagi Grimberg <sagi@grimberg.me>
Cc: Justin Tee <justin.tee@broadcom.com>,
Naresh Gottumukkala <nareshgottumukkala83@gmail.com>,
Paul Ely <paul.ely@broadcom.com>,
Hannes Reinecke <hare@kernel.org>,
Chaitanya Kulkarni <kch@nvidia.com>,
James Smart <jsmart833426@gmail.com>,
Randy Jennings <randyj@purestorage.com>,
Mohamed Khalfella <mkhalfella@purestorage.com>,
linux-nvme@lists.infradead.org, linux-kernel@vger.kernel.org
Subject: [PATCH 13/18] nvme-fc: perform error recovery directly from ioerr_work
Date: Fri, 18 Sep 2026 11:14:13 -0700 [thread overview]
Message-ID: <20260918181614.3947933-14-mkhalfella@purestorage.com> (raw)
In-Reply-To: <20260918181614.3947933-1-mkhalfella@purestorage.com>
Now that nvme_fc_start_ioerr_recovery() moves the controller to
RESETTING before queueing ioerr_work, the nvme_reset_ctrl() call in
nvme_fc_error_recovery() fails with -EBUSY and no recovery runs.
Do the recovery in the work itself instead: stop the controller, tear
down the association, move to CONNECTING and schedule a reconnect,
same as nvme_fc_reset_ctrl_work(). If the CONNECTING transition fails
the controller is being deleted and the delete path finishes the job.
This is how rdma and tcp structure their error recovery too.
The work now re-checks the controller state when it runs. A work
queued while CONNECTING can execute after the connect succeeded and
the controller went LIVE (a timed out connect command that completes
right after the timeout fires). Claim RESETTING in that case; if that
fails, a concurrent reset or delete owns the controller and will
complete the outstanding IOs.
Signed-off-by: Mohamed Khalfella <mkhalfella@purestorage.com>
---
drivers/nvme/host/fc.c | 96 +++++++++++++++++++++++++++---------------
1 file changed, 61 insertions(+), 35 deletions(-)
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 6181cb7ea8ce..5a530aa37641 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -229,6 +229,8 @@ static struct device *fc_udev_device;
static void nvme_fc_complete_rq(struct request *rq);
static void nvme_fc_start_ioerr_recovery(struct nvme_fc_ctrl *ctrl,
char *errmsg);
+static void __nvme_fc_abort_outstanding_ios(struct nvme_fc_ctrl *ctrl,
+ bool start_queues);
/* *********************** FC-NVME Port Management ************************ */
@@ -987,7 +989,7 @@ fc_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
static void nvme_fc_ctrl_put(struct nvme_fc_ctrl *);
static int nvme_fc_ctrl_get(struct nvme_fc_ctrl *);
-static void nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl, char *errmsg);
+static void nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl);
static void
__nvme_fc_finish_ls_req(struct nvmefc_ls_req_op *lsop)
@@ -1873,8 +1875,44 @@ nvme_fc_ctrl_ioerr_work(struct work_struct *work)
{
struct nvme_fc_ctrl *ctrl =
container_of(work, struct nvme_fc_ctrl, ioerr_work);
+ enum nvme_ctrl_state state = nvme_ctrl_state(&ctrl->ctrl);
+
+ /*
+ * if an error (io timeout, etc) while (re)connecting, the remote
+ * port requested terminating of the association (disconnect_ls)
+ * or an error (timeout or abort) occurred on an io while creating
+ * the controller. Abort any ios on the association and let the
+ * create_association error path resolve things.
+ */
+ if (state == NVME_CTRL_CONNECTING) {
+ __nvme_fc_abort_outstanding_ios(ctrl, true);
+ dev_warn(ctrl->ctrl.device,
+ "NVME-FC{%d}: transport error during (re)connect\n",
+ ctrl->cnum);
+ return;
+ }
+
+ /*
+ * Tear the association down only if this work owns recovery via a
+ * RESETTING claim, or if the delete path is waiting for IOs to
+ * complete.
+ */
+ if (state == NVME_CTRL_LIVE &&
+ nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_RESETTING))
+ state = NVME_CTRL_RESETTING;
- nvme_fc_error_recovery(ctrl, "transport detected io error");
+ switch (state) {
+ case NVME_CTRL_RESETTING:
+ case NVME_CTRL_DELETING:
+ case NVME_CTRL_DELETING_NOIO:
+ nvme_fc_error_recovery(ctrl);
+ break;
+ default:
+ dev_warn(ctrl->ctrl.device,
+ "NVME-FC{%d}: error recovery skipped, state %d owns recovery\n",
+ ctrl->cnum, nvme_ctrl_state(&ctrl->ctrl));
+ break;
+ }
}
/*
@@ -2533,39 +2571,6 @@ __nvme_fc_abort_outstanding_ios(struct nvme_fc_ctrl *ctrl, bool start_queues)
nvme_unquiesce_admin_queue(&ctrl->ctrl);
}
-static void
-nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl, char *errmsg)
-{
- enum nvme_ctrl_state state = nvme_ctrl_state(&ctrl->ctrl);
-
- /*
- * if an error (io timeout, etc) while (re)connecting, the remote
- * port requested terminating of the association (disconnect_ls)
- * or an error (timeout or abort) occurred on an io while creating
- * the controller. Abort any ios on the association and let the
- * create_association error path resolve things.
- */
- if (state == NVME_CTRL_CONNECTING) {
- __nvme_fc_abort_outstanding_ios(ctrl, true);
- dev_warn(ctrl->ctrl.device,
- "NVME-FC{%d}: transport error during (re)connect\n",
- ctrl->cnum);
- return;
- }
-
- /* Otherwise, only proceed if in LIVE state - e.g. on first error */
- if (state != NVME_CTRL_LIVE)
- return;
-
- dev_warn(ctrl->ctrl.device,
- "NVME-FC{%d}: transport association event: %s\n",
- ctrl->cnum, errmsg);
- dev_warn(ctrl->ctrl.device,
- "NVME-FC{%d}: resetting controller\n", ctrl->cnum);
-
- nvme_reset_ctrl(&ctrl->ctrl);
-}
-
static enum blk_eh_timer_return nvme_fc_timeout(struct request *rq)
{
struct nvme_fc_fcp_op *op = blk_mq_rq_to_pdu(rq);
@@ -3389,6 +3394,27 @@ nvme_fc_reset_ctrl_work(struct work_struct *work)
}
}
+static void
+nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl)
+{
+ nvme_stop_keep_alive(&ctrl->ctrl);
+ flush_work(&ctrl->ctrl.async_event_work);
+
+ /* will block while waiting for io to terminate */
+ nvme_fc_delete_association(ctrl);
+ nvme_stop_ctrl(&ctrl->ctrl);
+
+ /* Do not reconnect if controller is being deleted */
+ if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING))
+ return;
+
+ if (ctrl->rport->remoteport.port_state == FC_OBJSTATE_ONLINE) {
+ queue_delayed_work(nvme_wq, &ctrl->connect_work, 0);
+ return;
+ }
+
+ nvme_fc_reconnect_or_delete(ctrl, -ENOTCONN);
+}
static const struct nvme_ctrl_ops nvme_fc_ctrl_ops = {
.name = "fc",
--
2.55.0
next prev parent reply other threads:[~2026-09-18 18:17 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-18 18:14 [PATCH 00/18] TP8028 Rapid Path Failure Recovery Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 01/18] nvmet: Rapid Path Failure Recovery set controller identify fields Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 02/18] nvmet/debugfs: Export controller CIU and CIRN via debugfs Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 03/18] nvmet: Implement CCR nvme command Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 04/18] nvmet: Implement CCR logpage Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 05/18] nvmet: Send an AEN on CCR completion Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 06/18] nvme: Rapid Path Failure Recovery read controller identify fields Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 07/18] nvme: Introduce FENCING and FENCED controller states Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 08/18] nvme: Implement cross-controller reset recovery Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 09/18] nvme: Implement cross-controller reset completion Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 10/18] nvme-tcp: Use CCR to recover controller that hits an error Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 11/18] nvme-rdma: " Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 12/18] nvme-fc: start error recovery instead of aborting timed out IOs Mohamed Khalfella
2026-09-18 18:14 ` Mohamed Khalfella [this message]
2026-09-18 18:14 ` [PATCH 14/18] nvme-fc: Use CCR to recover controller that hits an error Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 15/18] nvme-fc: Hold inflight requests while in FENCING state Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 16/18] nvmet: Add support for CQT to nvme target Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 17/18] nvme: Add support for CQT to nvme host Mohamed Khalfella
2026-09-18 18:14 ` [PATCH 18/18] nvme: let controller deletion wait out a fencing window Mohamed Khalfella
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=20260918181614.3947933-14-mkhalfella@purestorage.com \
--to=mkhalfella@purestorage.com \
--cc=axboe@kernel.dk \
--cc=hare@kernel.org \
--cc=hch@lst.de \
--cc=jsmart833426@gmail.com \
--cc=justin.tee@broadcom.com \
--cc=kbusch@kernel.org \
--cc=kch@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-nvme@lists.infradead.org \
--cc=nareshgottumukkala83@gmail.com \
--cc=paul.ely@broadcom.com \
--cc=randyj@purestorage.com \
--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 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®