[PATCH v6 15/18] nvme-fc: Hold inflight requests while in FENCING state
Mohamed Khalfella
mkhalfella at purestorage.com
Tue Nov 3 10:45:07 PST 2026
On Tue 2026-11-03 11:51:28 +0100, Daniel Wagner wrote:
> On Mon, Nov 02, 2026 at 05:18:51PM -0800, Mohamed Khalfella wrote:
> > -/* Complete the requests held in __nvme_fc_teardown_association() */
> > +static bool
> > +nvme_fc_cancel_held_request(struct request *req, void *data)
> > +{
> > + struct nvme_fc_fcp_op *op = blk_mq_rq_to_pdu(req);
> > + struct nvme_ctrl *ctrl = data;
> > +
> > + /*
> > + * A single cmpxchg decides who completes this request. If the op is
> > + * HELD, nvme_fc_fcpio_done() is done with it and we cancel it. If it
> > + * is still COMPLETE, nvme_fc_fcpio_done() has not decided yet: move
> > + * it to IDLE so its own cmpxchg to HELD fails and it completes the
> > + * request itself (nvme_fc_complete_rq() sets IDLE anyway). Doing this
> > + * as read-then-cmpxchg would let the op become HELD in between and
> > + * leave it with no one to complete it.
> > + */
> > + if (atomic_cmpxchg(&op->state, FCPOP_STATE_COMPLETE,
> > + FCPOP_STATE_IDLE) == FCPOP_STATE_HELD)
> > + nvme_cancel_request(req, ctrl);
> > +
> > + return true;
> > +}
> > +
> > static void
> > -nvme_fc_flush_held_requests(struct nvme_fc_ctrl *ctrl)
> > +nvme_fc_cancel_held_requests(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set)
> > {
> > - nvme_cancel_tagset(&ctrl->ctrl);
> > - nvme_cancel_admin_tagset(&ctrl->ctrl);
> > + if (set) {
>
> Do we call flush when we don't have a set?
Yes, that is possible in case the controller fail first connect attempt
before the IO tagset is created. Commit ee38469f8849 ("nvme-fc: Do not
cancel requests in io target before it is initialized") added similar
check to __nvme_fc_abort_outstanding_ios() to close this gap.
>
> nitpick: use the early return pattern if needed.
Done.
>
> > + blk_mq_tagset_busy_iter(set, nvme_fc_cancel_held_request, ctrl);
> > + blk_mq_tagset_wait_completed_request(set);
> > + }
> > +}
> > +
> > +/* Complete the requests nvme_fc_fcpio_done() held during the fence */
> > +static void
> > +nvme_fc_flush_held_requests(struct nvme_fc_ctrl *fc_ctrl)
> > +{
> > + struct nvme_ctrl *ctrl = &fc_ctrl->ctrl;
> > +
> > + nvme_fc_cancel_held_requests(ctrl, ctrl->tagset);
> > + nvme_fc_cancel_held_requests(ctrl, ctrl->admin_tagset);
> >
> > /* re-enable the admin_q so anything new can fast fail */
> > - nvme_unquiesce_admin_queue(&ctrl->ctrl);
> > + nvme_unquiesce_admin_queue(ctrl);
> >
> > /* resume the io queues so that things will fast fail */
> > - nvme_unquiesce_io_queues(&ctrl->ctrl);
> > + nvme_unquiesce_io_queues(ctrl);
> > }
> >
> > /*
>
> Yes, I'd prefer this over the introduction of RCU in this code path.
>
Okay. I spent a lot of time thinking about these two atomic_cmpxchg() to
make sure they cover all scenarios. That is holding all requests to be
held and at the same time not leave a request in a held state and not
get to complete it. Let me know if you think I missed a scenario.
More information about the Linux-nvme
mailing list