[PATCH v6 08/18] nvme: Implement cross-controller reset recovery
Mohamed Khalfella
mkhalfella at purestorage.com
Sat Oct 31 01:21:55 PDT 2026
On Tue 2026-10-20 10:55:10 +0200, Daniel Wagner wrote:
> > +static int nvme_issue_wait_ccr(struct nvme_ctrl *sctrl, struct nvme_ctrl *ictrl,
> > + unsigned long deadline)
> > +{
> > + struct nvme_ccr_entry ccr = { };
> > + union nvme_result res = { 0 };
> > + struct nvme_command c = { };
> > + unsigned long flags, now, tmo = 0;
> > + bool completed = false;
> > + int ret = 0;
> > + u32 result;
> > +
> > + init_completion(&ccr.complete);
> > + ccr.ictrl = ictrl;
> > +
> > + spin_lock_irqsave(&sctrl->lock, flags);
> > + list_add_tail(&ccr.list, &sctrl->ccr_list);
> > + spin_unlock_irqrestore(&sctrl->lock, flags);
> > +
> > + c.ccr.opcode = nvme_admin_cross_ctrl_reset;
> > + c.ccr.ciu = ictrl->ciu;
> > + c.ccr.icid = cpu_to_le16(ictrl->cntlid);
> > + c.ccr.cirn = cpu_to_le64(ictrl->cirn);
> > + ret = __nvme_submit_sync_cmd(sctrl->admin_q, &c, &res,
> > + NULL, 0, NVME_QID_ANY, 0);
> > + if (ret) {
> > + ret = -EIO;
> > + goto out;
> > + }
> > +
> > + result = le32_to_cpu(res.u32);
> > + if (result & 0x01) /* Immediate Reset Successful */
> > + goto out;
> > +
> > + now = jiffies;
> > + if (time_before(now, deadline))
> > + tmo = min_t(unsigned long,
> > + secs_to_jiffies(ictrl->kato), deadline - now);
> > +
> > + if (!wait_for_completion_timeout(&ccr.complete, tmo)) {
> > + ret = -ETIMEDOUT;
> > + goto out;
> > + }
> > +
> > + completed = true;
> > +
> > +out:
> > + spin_lock_irqsave(&sctrl->lock, flags);
> > + list_del(&ccr.list);
> > + spin_unlock_irqrestore(&sctrl->lock, flags);
> > + if (completed) {
> > + if (ccr.ccrs == NVME_CCR_STATUS_SUCCESS)
> > + return 0;
> > + return -EREMOTEIO;
> > + }
> > + return ret;
>
>
> This part could be made a bit simpler and there is no need for the local
> completed variable:
>
> [...]
> if (!wait_for_completion_timeout(&ccr.complete, tmo))
> ret = -ETIMEDOUT;
>
> out:
> spin_lock_irqsave(&sctrl->lock, flags);
> list_del(&ccr.list);
> spin_unlock_irqrestore(&sctrl->lock, flags);
>
> if (ret || !completion_done(&ccr.complete))
> return ret;
> if (ccr.ccrs == NVME_CCR_STATUS_SUCCESS)
> return 0;
> return -EREMOTEIO;
> }
701
702 if (!wait_for_completion_timeout(&ccr.complete, tmo))
703 ret = -ETIMEDOUT;
704
705 out:
706 spin_lock_irqsave(&sctrl->lock, flags);
707 list_del(&ccr.list);
708 spin_unlock_irqrestore(&sctrl->lock, flags);
709
710 if (ret || !completion_done(&ccr.complete))
711 return ret;
712 if (ccr.ccrs == NVME_CCR_STATUS_SUCCESS)
713 return 0;
714 return -EREMOTEIO;
715 }
Actually, this code has a bug. It does not correctly handle the case
when CCR operation completes with an error. I added line numbers to show
that.
- line 701: AEN, omplete(ccr.complete), x->done++, x->done = 1
- line 702: wait_for_completion_timeout(), x->done--, x->done = 0
- line 710: ret == 0, x->done == 0, completion_done() returns false.
- line 711: return 0;
The problem is that wait_for_completion_timeout() consumes the
completion. This causes completion_done() to return false leading us to
believe CCR operation has not completed.
More information about the Linux-nvme
mailing list