From: Mohamed Khalfella <mkhalfella@purestorage.com>
To: Hannes Reinecke <hare@suse.de>
Cc: Justin Tee <justin.tee@broadcom.com>,
Naresh Gottumukkala <nareshgottumukkala83@gmail.com>,
Paul Ely <paul.ely@broadcom.com>,
Chaitanya Kulkarni <kch@nvidia.com>, Jens Axboe <axboe@kernel.dk>,
Keith Busch <kbusch@kernel.org>, Sagi Grimberg <sagi@grimberg.me>,
James Smart <jsmart833426@gmail.com>,
Aaron Dailey <adailey@purestorage.com>,
Randy Jennings <randyj@purestorage.com>,
Dhaval Giani <dgiani@purestorage.com>,
linux-nvme@lists.infradead.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v4 03/15] nvmet: Implement CCR nvme command
Date: Tue, 31 Mar 2026 09:38:06 -0700 [thread overview]
Message-ID: <20260331163806.GB2861-mkhalfella@purestorage.com> (raw)
In-Reply-To: <204893dd-d256-4f37-955a-1fa724048021@suse.de>
On Mon 2026-03-30 12:45:57 +0200, Hannes Reinecke wrote:
> On 3/28/26 01:43, Mohamed Khalfella wrote:
> > Defined by TP8028 Rapid Path Failure Recovery, CCR (Cross-Controller
> > Reset) command is an nvme command issued to source controller by
> > initiator to reset impacted controller. Implement CCR command for linux
> > nvme target.
> >
> > Signed-off-by: Mohamed Khalfella <mkhalfella@purestorage.com>
> > ---
> > drivers/nvme/target/admin-cmd.c | 74 ++++++++++++++++++++++++++++++++
> > drivers/nvme/target/core.c | 76 +++++++++++++++++++++++++++++++++
> > drivers/nvme/target/nvmet.h | 13 ++++++
> > include/linux/nvme.h | 23 ++++++++++
> > 4 files changed, 186 insertions(+)
> >
> > diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
> > index ec09e30eca18..0a37c0eeebb5 100644
> > --- a/drivers/nvme/target/admin-cmd.c
> > +++ b/drivers/nvme/target/admin-cmd.c
> > @@ -376,7 +376,9 @@ static void nvmet_get_cmd_effects_admin(struct nvmet_ctrl *ctrl,
> > log->acs[nvme_admin_get_features] =
> > log->acs[nvme_admin_async_event] =
> > log->acs[nvme_admin_keep_alive] =
> > + log->acs[nvme_admin_cross_ctrl_reset] =
> > cpu_to_le32(NVME_CMD_EFFECTS_CSUPP);
> > +
> > }
> >
> > static void nvmet_get_cmd_effects_nvm(struct nvme_effects_log *log)
> > @@ -1613,6 +1615,75 @@ void nvmet_execute_keep_alive(struct nvmet_req *req)
> > nvmet_req_complete(req, status);
> > }
> >
> > +void nvmet_execute_cross_ctrl_reset(struct nvmet_req *req)
> > +{
> > + struct nvmet_ctrl *ictrl, *sctrl = req->sq->ctrl;
> > + struct nvme_command *cmd = req->cmd;
> > + struct nvmet_ccr *ccr, *new_ccr;
> > + int ccr_active, ccr_total;
> > + u16 cntlid, status = NVME_SC_SUCCESS;
> > +
> > + cntlid = le16_to_cpu(cmd->ccr.icid);
> > + if (sctrl->cntlid == cntlid) {
> > + req->error_loc =
> > + offsetof(struct nvme_cross_ctrl_reset_cmd, icid);
> > + status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
> > + goto out;
> > + }
> > +
> > + /* Find and get impacted controller */
> > + ictrl = nvmet_ctrl_find_get_ccr(sctrl->subsys, sctrl->hostnqn,
> > + cmd->ccr.ciu, cntlid,
> > + le64_to_cpu(cmd->ccr.cirn));
> > + if (!ictrl) {
> > + /* Immediate Reset Successful */
> > + nvmet_set_result(req, 1);
> > + status = NVME_SC_SUCCESS;
> > + goto out;
> > + }
> > +
> > + ccr_total = ccr_active = 0;
> > + mutex_lock(&sctrl->lock);
> > + list_for_each_entry(ccr, &sctrl->ccr_list, entry) {
> > + if (ccr->ctrl == ictrl) {
> > + status = NVME_SC_CCR_IN_PROGRESS | NVME_STATUS_DNR;
> > + goto out_unlock;
> > + }
> > +
> > + ccr_total++;
> > + if (ccr->ctrl)
> > + ccr_active++;
> > + }
> > +
> > + if (ccr_active >= NVMF_CCR_LIMIT) {
> > + status = NVME_SC_CCR_LIMIT_EXCEEDED;
> > + goto out_unlock;
> > + }
> > + if (ccr_total >= NVMF_CCR_PER_PAGE) {
> > + status = NVME_SC_CCR_LOGPAGE_FULL;
> > + goto out_unlock;
> > + }
> > +
> > + new_ccr = kmalloc_obj(*new_ccr, GFP_KERNEL);
> > + if (!new_ccr) {
> > + status = NVME_SC_INTERNAL;
> > + goto out_unlock;
> > + }
> > +
> > + new_ccr->ciu = cmd->ccr.ciu;
> > + new_ccr->icid = cntlid;
> > + new_ccr->ctrl = ictrl;
> > + list_add_tail(&new_ccr->entry, &sctrl->ccr_list);
> > +
> > +out_unlock:
> > + mutex_unlock(&sctrl->lock);
> > + if (status == NVME_SC_SUCCESS)
> > + nvmet_ctrl_fatal_error(ictrl);
> > + nvmet_ctrl_put(ictrl);
> > +out:
> > + nvmet_req_complete(req, status);
> > +}
> > +
> > u32 nvmet_admin_cmd_data_len(struct nvmet_req *req)
> > {
> > struct nvme_command *cmd = req->cmd;
> > @@ -1690,6 +1761,9 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)
> > case nvme_admin_keep_alive:
> > req->execute = nvmet_execute_keep_alive;
> > return 0;
> > + case nvme_admin_cross_ctrl_reset:
> > + req->execute = nvmet_execute_cross_ctrl_reset;
> > + return 0;
> > default:
> > return nvmet_report_invalid_opcode(req);
> > }
> > diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
> > index e8b945a01f35..2e0c31d82bad 100644
> > --- a/drivers/nvme/target/core.c
> > +++ b/drivers/nvme/target/core.c
> > @@ -117,6 +117,20 @@ u16 nvmet_zero_sgl(struct nvmet_req *req, off_t off, size_t len)
> > return 0;
> > }
> >
> > +void nvmet_ctrl_cleanup_ccrs(struct nvmet_ctrl *ctrl, bool all)
> > +{
> > + struct nvmet_ccr *ccr, *tmp;
> > +
> > + lockdep_assert_held(&ctrl->lock);
> > +
> > + list_for_each_entry_safe(ccr, tmp, &ctrl->ccr_list, entry) {
> > + if (all || ccr->ctrl == NULL) {
> > + list_del(&ccr->entry);
> > + kfree(ccr);
> > + }
> > + }
> > +}
> > +
> > static u32 nvmet_max_nsid(struct nvmet_subsys *subsys)
> > {
> > struct nvmet_ns *cur;
> > @@ -1399,6 +1413,7 @@ static void nvmet_start_ctrl(struct nvmet_ctrl *ctrl)
> > if (!nvmet_is_disc_subsys(ctrl->subsys)) {
> > ctrl->ciu = ((u8)(ctrl->ciu + 1)) ? : 1;
> > ctrl->cirn = get_random_u64();
> > + nvmet_ctrl_cleanup_ccrs(ctrl, false);
> > }
> > ctrl->csts = NVME_CSTS_RDY;
> >
> > @@ -1504,6 +1519,35 @@ struct nvmet_ctrl *nvmet_ctrl_find_get(const char *subsysnqn,
> > return ctrl;
> > }
> >
> > +struct nvmet_ctrl *nvmet_ctrl_find_get_ccr(struct nvmet_subsys *subsys,
> > + const char *hostnqn, u8 ciu,
> > + u16 cntlid, u64 cirn)
> > +{
> > + struct nvmet_ctrl *ctrl, *ictrl = NULL;
> > + bool found = false;
> > +
> > + mutex_lock(&subsys->lock);
> > + list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry) {
> > + if (ctrl->cntlid != cntlid)
> > + continue;
> > +
> > + /* Avoid racing with a controller that is becoming ready */
> > + mutex_lock(&ctrl->lock);
> > + if (ctrl->ciu == ciu && ctrl->cirn == cirn)
> > + found = true;
> > + mutex_unlock(&ctrl->lock);
> > +
> > + if (found) {
> > + if (kref_get_unless_zero(&ctrl->ref))
> > + ictrl = ctrl;
> > + break;
> > + }
> > + };
> > + mutex_unlock(&subsys->lock);
> > +
> > + return ictrl;
> > +}
> > +
> > u16 nvmet_check_ctrl_status(struct nvmet_req *req)
> > {
> > if (unlikely(!(req->sq->ctrl->cc & NVME_CC_ENABLE))) {
> > @@ -1629,6 +1673,7 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
> > subsys->clear_ids = 1;
> > #endif
> >
> > + INIT_LIST_HEAD(&ctrl->ccr_list);
> > INIT_WORK(&ctrl->async_event_work, nvmet_async_event_work);
> > INIT_LIST_HEAD(&ctrl->async_events);
> > INIT_RADIX_TREE(&ctrl->p2p_ns_map, GFP_KERNEL);
> > @@ -1739,12 +1784,43 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
> > }
> > EXPORT_SYMBOL_GPL(nvmet_alloc_ctrl);
> >
> > +static void nvmet_ctrl_complete_pending_ccr(struct nvmet_ctrl *ctrl)
> > +{
> > + struct nvmet_subsys *subsys = ctrl->subsys;
> > + struct nvmet_ctrl *sctrl;
> > + struct nvmet_ccr *ccr;
> > +
> > + lockdep_assert_held(&subsys->lock);
> > +
> > + /* Cleanup all CCRs issued by ctrl as source controller */
> > + mutex_lock(&ctrl->lock);
> > + nvmet_ctrl_cleanup_ccrs(ctrl, true);
> > + mutex_unlock(&ctrl->lock);
> > +
> > + /*
> > + * Find all CCRs targeting ctrl as impacted controller and
> > + * set ccr->ctrl to NULL. This tells the source controller
> > + * that CCR completed successfully.
> > + */
> > + list_for_each_entry(sctrl, &subsys->ctrls, subsys_entry) {
> > + mutex_lock(&sctrl->lock);
> > + list_for_each_entry(ccr, &sctrl->ccr_list, entry) {
> > + if (ccr->ctrl == ctrl) {
> > + ccr->ctrl = NULL;
> > + break;
> > + }
> > + }
> > + mutex_unlock(&sctrl->lock);
> > + }
> > +}
> > +
>
> Do I see this correct that with this implementation a CCR is only
> complete once the controller resets? IOW the CCR has to wait for
> the controller to be reset, but it does not invoke a controller reset
> itself?
>
> Is that intended?
nvmet_execute_cross_ctrl_reset() calls nvmet_ctrl_fatal_error() to cause
impacted controller to fail. CCR is completed when the impacted
controller exits.
next prev parent reply other threads:[~2026-03-31 16:38 UTC|newest]
Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-28 0:43 [PATCH v4 00/15] TP8028 Rapid Path Failure Recovery Mohamed Khalfella
2026-03-28 0:43 ` [PATCH v4 01/15] nvmet: Rapid Path Failure Recovery set controller identify fields Mohamed Khalfella
2026-03-30 10:37 ` Hannes Reinecke
2026-05-15 2:08 ` Randy Jennings
2026-03-28 0:43 ` [PATCH v4 02/15] nvmet/debugfs: Export controller CIU and CIRN via debugfs Mohamed Khalfella
2026-05-14 23:42 ` Randy Jennings
2026-03-28 0:43 ` [PATCH v4 03/15] nvmet: Implement CCR nvme command Mohamed Khalfella
2026-03-30 10:45 ` Hannes Reinecke
2026-03-31 16:38 ` Mohamed Khalfella [this message]
2026-04-07 5:40 ` Hannes Reinecke
2026-05-15 0:18 ` Randy Jennings
2026-03-28 0:43 ` [PATCH v4 04/15] nvmet: Implement CCR logpage Mohamed Khalfella
2026-05-15 0:38 ` Randy Jennings
2026-03-28 0:43 ` [PATCH v4 05/15] nvmet: Send an AEN on CCR completion Mohamed Khalfella
2026-05-15 0:50 ` Randy Jennings
2026-03-28 0:43 ` [PATCH v4 06/15] nvme: Rapid Path Failure Recovery read controller identify fields Mohamed Khalfella
2026-05-15 2:03 ` Randy Jennings
2026-03-28 0:43 ` [PATCH v4 07/15] nvme: Introduce FENCING and FENCED controller states Mohamed Khalfella
2026-03-30 10:46 ` Hannes Reinecke
2026-05-15 2:06 ` Randy Jennings
2026-03-28 0:43 ` [PATCH v4 08/15] nvme: Implement cross-controller reset recovery Mohamed Khalfella
2026-03-30 10:50 ` Hannes Reinecke
2026-03-31 16:47 ` Mohamed Khalfella
2026-04-07 5:39 ` Hannes Reinecke
2026-04-07 20:46 ` Mohamed Khalfella
2026-04-13 15:25 ` Randy Jennings
2026-04-13 16:33 ` Mohamed Khalfella
2026-04-24 23:07 ` Randy Jennings
2026-03-28 0:43 ` [PATCH v4 09/15] nvme: Implement cross-controller reset completion Mohamed Khalfella
2026-03-30 10:53 ` Hannes Reinecke
2026-03-31 16:55 ` Mohamed Khalfella
2026-04-07 5:48 ` Hannes Reinecke
2026-04-07 19:09 ` Mohamed Khalfella
2026-03-28 0:43 ` [PATCH v4 10/15] nvme-tcp: Use CCR to recover controller that hits an error Mohamed Khalfella
2026-03-30 11:00 ` Hannes Reinecke
2026-03-28 0:43 ` [PATCH v4 11/15] nvme-rdma: " Mohamed Khalfella
2026-03-28 0:43 ` [PATCH v4 12/15] nvme-fc: Refactor IO error recovery Mohamed Khalfella
2026-03-28 0:43 ` [PATCH v4 13/15] nvme-fc: Use CCR to recover controller that hits an error Mohamed Khalfella
2026-03-28 0:43 ` [PATCH v4 14/15] nvme-fc: Hold inflight requests while in FENCING state Mohamed Khalfella
2026-03-28 0:43 ` [PATCH v4 15/15] nvme-fc: Do not cancel requests in io taget before it is initialized Mohamed Khalfella
2026-05-12 21:40 ` [PATCH v4 00/15] TP8028 Rapid Path Failure Recovery Mohamed Khalfella
2026-05-12 22:02 ` Sagi Grimberg
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=20260331163806.GB2861-mkhalfella@purestorage.com \
--to=mkhalfella@purestorage.com \
--cc=adailey@purestorage.com \
--cc=axboe@kernel.dk \
--cc=dgiani@purestorage.com \
--cc=hare@suse.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 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.