[PATCH v3 10/21] nvme-tcp: Use CCR to recover controller that hits an error
Hannes Reinecke
hare at suse.de
Mon Feb 16 04:47:08 PST 2026
On 2/14/26 05:25, Mohamed Khalfella wrote:
> An alive nvme controller that hits an error now will move to FENCING
> state instead of RESETTING state. ctrl->fencing_work attempts CCR to
> terminate inflight IOs. Regardless of the success or failure of CCR
> operation the controller is transitioned to RESETTING state to continue
> error recovery process.
>
> Signed-off-by: Mohamed Khalfella <mkhalfella at purestorage.com>
> ---
> drivers/nvme/host/tcp.c | 32 +++++++++++++++++++++++++++++++-
> 1 file changed, 31 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
> index 69cb04406b47..229cfdffd848 100644
> --- a/drivers/nvme/host/tcp.c
> +++ b/drivers/nvme/host/tcp.c
> @@ -193,6 +193,7 @@ struct nvme_tcp_ctrl {
> struct sockaddr_storage src_addr;
> struct nvme_ctrl ctrl;
>
> + struct work_struct fencing_work;
> struct work_struct err_work;
> struct delayed_work connect_work;
> struct nvme_tcp_request async_req;
> @@ -611,6 +612,12 @@ static void nvme_tcp_init_recv_ctx(struct nvme_tcp_queue *queue)
>
> static void nvme_tcp_error_recovery(struct nvme_ctrl *ctrl)
> {
> + if (nvme_change_ctrl_state(ctrl, NVME_CTRL_FENCING)) {
> + dev_warn(ctrl->device, "starting controller fencing\n");
> + queue_work(nvme_wq, &to_tcp_ctrl(ctrl)->fencing_work);
> + return;
> + }
> +
> if (!nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING))
> return;
>
> @@ -2470,12 +2477,31 @@ static void nvme_tcp_reconnect_ctrl_work(struct work_struct *work)
> nvme_tcp_reconnect_or_remove(ctrl, ret);
> }
>
> +static void nvme_tcp_fencing_work(struct work_struct *work)
> +{
> + struct nvme_tcp_ctrl *tcp_ctrl = container_of(work,
> + struct nvme_tcp_ctrl, fencing_work);
> + struct nvme_ctrl *ctrl = &tcp_ctrl->ctrl;
> + unsigned long rem;
> +
> + rem = nvme_fence_ctrl(ctrl);
> + if (rem) {
> + dev_info(ctrl->device,
> + "CCR failed, skipping time-based recovery\n");
> + }
> +
> + nvme_change_ctrl_state(ctrl, NVME_CTRL_FENCED);
> + if (nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING))
> + queue_work(nvme_reset_wq, &tcp_ctrl->err_work);
> +}
> +
> static void nvme_tcp_error_recovery_work(struct work_struct *work)
> {
> struct nvme_tcp_ctrl *tcp_ctrl = container_of(work,
> struct nvme_tcp_ctrl, err_work);
> struct nvme_ctrl *ctrl = &tcp_ctrl->ctrl;
>
> + flush_work(&to_tcp_ctrl(ctrl)->fencing_work);
> if (nvme_tcp_key_revoke_needed(ctrl))
> nvme_auth_revoke_tls_key(ctrl);
> nvme_stop_keep_alive(ctrl);
> @@ -2518,6 +2544,7 @@ static void nvme_reset_ctrl_work(struct work_struct *work)
> container_of(work, struct nvme_ctrl, reset_work);
> int ret;
>
> + flush_work(&to_tcp_ctrl(ctrl)->fencing_work);
> if (nvme_tcp_key_revoke_needed(ctrl))
> nvme_auth_revoke_tls_key(ctrl);
> nvme_stop_ctrl(ctrl);
> @@ -2643,13 +2670,15 @@ static enum blk_eh_timer_return nvme_tcp_timeout(struct request *rq)
> struct nvme_tcp_cmd_pdu *pdu = nvme_tcp_req_cmd_pdu(req);
> struct nvme_command *cmd = &pdu->cmd;
> int qid = nvme_tcp_queue_id(req->queue);
> + enum nvme_ctrl_state state;
>
> dev_warn(ctrl->device,
> "I/O tag %d (%04x) type %d opcode %#x (%s) QID %d timeout\n",
> rq->tag, nvme_cid(rq), pdu->hdr.type, cmd->common.opcode,
> nvme_fabrics_opcode_str(qid, cmd), qid);
>
> - if (nvme_ctrl_state(ctrl) != NVME_CTRL_LIVE) {
> + state = nvme_ctrl_state(ctrl);
> + if (state != NVME_CTRL_LIVE && state != NVME_CTRL_FENCING) {
> /*
> * If we are resetting, connecting or deleting we should
> * complete immediately because we may block controller
> @@ -2904,6 +2933,7 @@ static struct nvme_tcp_ctrl *nvme_tcp_alloc_ctrl(struct device *dev,
>
> INIT_DELAYED_WORK(&ctrl->connect_work,
> nvme_tcp_reconnect_ctrl_work);
> + INIT_WORK(&ctrl->fencing_work, nvme_tcp_fencing_work);
> INIT_WORK(&ctrl->err_work, nvme_tcp_error_recovery_work);
> INIT_WORK(&ctrl->ctrl.reset_work, nvme_reset_ctrl_work);
>
I still would love to have the 'FENCING/FENCED' state handled in the
generic code, but that would require quite some twiddling with the
transport-specific error handlings. So probably not for this round.
Other than that:
Reviewed-by: Hannes Reinecke <hare at suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare at 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
More information about the Linux-nvme
mailing list