Implement the NVMe FC-LS abort callback by issuing an ibmvfc cancel MAD
to the VIOS for the outstanding link-service request.

Use the saved event pointer from the original FC-LS request to identify
the command to cancel, submit the cancel operation, and complete the
abort request based on the returned status.

Signed-off-by: Tyrel Datwyler <[email protected]>
---
 drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 64 +++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c 
b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index bff469d0b47d..18e8657abc44 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -13,6 +13,8 @@
 
 #include "ibmvfc-nvme.h"
 
+static unsigned int default_timeout = IBMVFC_DEFAULT_TIMEOUT;
+
 static void ibmvfc_nvme_localport_delete(struct nvme_fc_local_port *lport)
 {
        struct ibmvfc_host *vhost = lport->private;
@@ -159,10 +161,72 @@ static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port 
*lport,
        return 0;
 }
 
+static void ibmvfc_sync_nvme_completion(struct ibmvfc_event *evt)
+{
+       /* copy the response back */
+       if (evt->sync_iu)
+               *evt->sync_iu = *evt->xfer_iu;
+
+       complete(&evt->comp);
+}
+
+static void ibmvfc_init_ls_abort(struct ibmvfc_event *evt, struct 
nvmefc_ls_req *ls_abort)
+{
+       struct ibmvfc_tmf *tmf;
+       struct ibmvfc_event *abt_evt = ls_abort->private;
+       struct ibmvfc_target *tgt = abt_evt->tgt;
+       struct ibmvfc_host *vhost = evt->vhost;
+
+       tmf = &evt->iu.tmf;
+       memset(tmf, 0, sizeof(*tmf));
+       tmf->common.version = cpu_to_be32(2);
+       tmf->target_wwpn = cpu_to_be64(tgt->wwpn);
+       tmf->common.opcode = cpu_to_be32(IBMVFC_NVMF_TMF_MAD);
+       tmf->common.length = cpu_to_be16(sizeof(*tmf));
+       if (vhost->state != IBMVFC_ACTIVE)
+               if (!ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPRESS_ABTS))
+                       tmf->flags = cpu_to_be32(IBMVFC_TMF_SUPPRESS_ABTS);
+       tmf->cancel_key = cpu_to_be32((u64)abt_evt);
+       tmf->my_cancel_key = cpu_to_be32((u64)evt);
+       tmf->assoc_id = cpu_to_be64(tgt->assoc_id);
+
+       init_completion(&evt->comp);
+}
+
 static void ibmvfc_nvme_ls_abort(struct nvme_fc_local_port *lport,
                                struct nvme_fc_remote_port *rport,
                                struct nvmefc_ls_req *ls_abort)
 {
+       struct ibmvfc_host *vhost = lport->private;
+       struct ibmvfc_target *tgt = rport->private;
+       struct ibmvfc_event *evt;
+       union ibmvfc_iu rsp;
+       unsigned long flags;
+       u16 status;
+
+       evt = ibmvfc_get_event(&vhost->crq);
+       if (!vhost->logged_in || !evt)
+               return;
+
+       spin_lock_irqsave(vhost->host->host_lock, flags);
+       kref_get(&tgt->kref);
+       ibmvfc_init_event(evt, ibmvfc_sync_nvme_completion, IBMVFC_MAD_FORMAT);
+       ibmvfc_init_ls_abort(evt, ls_abort);
+       evt->sync_iu = &rsp;
+
+       if (ibmvfc_send_event(evt, vhost, default_timeout))
+               goto out;
+
+       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+       wait_for_completion(&evt->comp);
+       status = be16_to_cpu(rsp.mad_common.status);
+       spin_lock_irqsave(vhost->host->host_lock, flags);
+       ibmvfc_free_event(evt);
+out:
+       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+       ibmvfc_dbg(vhost, "ls_abort: cancel failed with rc=%x\n", status);
+       kref_put(&tgt->kref, ibmvfc_release_tgt);
 }
 
 static void ibmvfc_nvme_done(struct ibmvfc_event *evt)
-- 
2.54.0


Reply via email to