nvmet_fc_free_fcp_iod(struct nvmet_fc_tgt_queue *queue,
                        struct nvmet_fc_fcp_iod *fod)
 {
+       struct nvmefc_tgt_fcp_req *fcpreq = fod->fcpreq;
+       struct nvmet_fc_tgtport *tgtport = fod->tgtport;
        unsigned long flags;
 
        spin_lock_irqsave(&queue->qlock, flags);
         * release the reference taken at queue lookup and fod allocation
         */
        nvmet_fc_tgt_q_put(queue);
+
+       tgtport->ops->fcp_req_release(&tgtport->fc_target_port, fcpreq);
 }
 
 static int
        int ret, idx;
 
        if (!template->xmt_ls_rsp || !template->fcp_op ||
-           !template->targetport_delete ||
+           !template->fcp_req_release || !template->targetport_delete ||
            !template->max_hw_queues || !template->max_sgl_segments ||
            !template->max_dif_sgl_segments || !template->dma_boundary) {
                ret = -EINVAL;
  * If this routine returns error, the lldd should abort the exchange.
  *
  * @target_port: pointer to the (registered) target port the FCP CMD IU
- *              was receive on.
+ *              was received on.
  * @fcpreq:     pointer to a fcpreq request structure to be used to reference
  *              the exchange corresponding to the FCP Exchange.
  * @cmdiubuf:   pointer to the buffer containing the FCP CMD IU
 
        tgt_fcpreq->fcp_error = fcp_err;
        tgt_fcpreq->done(tgt_fcpreq);
 
-       if ((!fcp_err) && (op == NVMET_FCOP_RSP ||
-                       op == NVMET_FCOP_READDATA_RSP ||
-                       op == NVMET_FCOP_ABORT))
-               schedule_work(&tfcp_req->work);
-
        return 0;
 }
 
+static void
+fcloop_fcp_req_release(struct nvmet_fc_target_port *tgtport,
+                       struct nvmefc_tgt_fcp_req *tgt_fcpreq)
+{
+       struct fcloop_fcpreq *tfcp_req = tgt_fcp_req_to_fcpreq(tgt_fcpreq);
+
+       schedule_work(&tfcp_req->work);
+}
+
 static void
 fcloop_ls_abort(struct nvme_fc_local_port *localport,
                        struct nvme_fc_remote_port *remoteport,
        .targetport_delete      = fcloop_targetport_delete,
        .xmt_ls_rsp             = fcloop_xmt_ls_rsp,
        .fcp_op                 = fcloop_fcp_op,
+       .fcp_req_release        = fcloop_fcp_req_release,
        .max_hw_queues          = FCLOOP_HW_QUEUES,
        .max_sgl_segments       = FCLOOP_SGL_SEGS,
        .max_dif_sgl_segments   = FCLOOP_SGL_SEGS,
 
                if (phba->ktime_on)
                        lpfc_nvmet_ktime(phba, ctxp);
 #endif
-               /* Let Abort cmpl repost the context */
-               if (!(ctxp->flag & LPFC_NVMET_ABORT_OP))
-                       lpfc_nvmet_rq_post(phba, ctxp, &ctxp->rqb_buffer->hbuf);
+               /* lpfc_nvmet_xmt_fcp_release() will recycle the context */
        } else {
                ctxp->entry_cnt++;
                start_clean = offsetof(struct lpfc_iocbq, wqe);
        complete(&tport->tport_unreg_done);
 }
 
+static void
+lpfc_nvmet_xmt_fcp_release(struct nvmet_fc_target_port *tgtport,
+                          struct nvmefc_tgt_fcp_req *rsp)
+{
+       struct lpfc_nvmet_tgtport *lpfc_nvmep = tgtport->private;
+       struct lpfc_nvmet_rcv_ctx *ctxp =
+               container_of(rsp, struct lpfc_nvmet_rcv_ctx, ctx.fcp_req);
+       struct lpfc_hba *phba = ctxp->phba;
+       unsigned long flags;
+       bool aborting = false;
+
+       spin_lock_irqsave(&ctxp->ctxlock, flags);
+       if (ctxp->flag & LPFC_NVMET_ABORT_OP) {
+               aborting = true;
+               ctxp->flag |= LPFC_NVMET_CTX_RLS;
+       }
+       spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+
+       if (aborting)
+               /* let the abort path do the real release */
+               return;
+
+       /* Sanity check */
+       if (ctxp->state != LPFC_NVMET_STE_DONE) {
+               atomic_inc(&lpfc_nvmep->xmt_fcp_drop);
+               lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR,
+                               "6117 Bad state IO x%x aborted\n",
+                               ctxp->oxid);
+       }
+
+       lpfc_nvmeio_data(phba, "NVMET FCP FREE: xri x%x ste %d\n", ctxp->oxid,
+                        ctxp->state, 0);
+
+       lpfc_nvmet_rq_post(phba, ctxp, &ctxp->rqb_buffer->hbuf);
+}
+
 static struct nvmet_fc_target_template lpfc_tgttemplate = {
        .targetport_delete = lpfc_nvmet_targetport_delete,
        .xmt_ls_rsp     = lpfc_nvmet_xmt_ls_rsp,
        .fcp_op         = lpfc_nvmet_xmt_fcp_op,
+       .fcp_req_release = lpfc_nvmet_xmt_fcp_release,
 
        .max_hw_queues  = 1,
        .max_sgl_segments = LPFC_NVMET_DEFAULT_SEGS,
        ctxp->wqeq = NULL;
        ctxp->state = LPFC_NVMET_STE_RCV;
        ctxp->rqb_buffer = (void *)nvmebuf;
+       spin_lock_init(&ctxp->ctxlock);
 
        lpfc_nvmeio_data(phba, "NVMET LS   RCV: xri x%x sz %d from %06x\n",
                         oxid, size, sid);
        struct lpfc_nvmet_rcv_ctx *ctxp;
        struct lpfc_nvmet_tgtport *tgtp;
        uint32_t status, result;
+       unsigned long flags;
+       bool released = false;
 
        ctxp = cmdwqe->context2;
        status = bf_get(lpfc_wcqe_c_status, wcqe);
                        result, wcqe->word3);
 
        ctxp->state = LPFC_NVMET_STE_DONE;
-       lpfc_nvmet_rq_post(phba, ctxp, &ctxp->rqb_buffer->hbuf);
+       spin_lock_irqsave(&ctxp->ctxlock, flags);
+       if (ctxp->flag & LPFC_NVMET_CTX_RLS)
+               released = true;
+       ctxp->flag &= ~LPFC_NVMET_ABORT_OP;
+       spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+
+       /*
+        * if transport has released ctx, then can reuse it. Otherwise,
+        * will be recycled by transport release call.
+        */
+       if (released)
+               lpfc_nvmet_rq_post(phba, ctxp, &ctxp->rqb_buffer->hbuf);
 
        cmdwqe->context2 = NULL;
        cmdwqe->context3 = NULL;
 {
        struct lpfc_nvmet_rcv_ctx *ctxp;
        struct lpfc_nvmet_tgtport *tgtp;
+       unsigned long flags;
        uint32_t status, result;
+       bool released = false;
 
        ctxp = cmdwqe->context2;
        status = bf_get(lpfc_wcqe_c_status, wcqe);
                                        ctxp->state, ctxp->oxid);
                }
                ctxp->state = LPFC_NVMET_STE_DONE;
-               lpfc_nvmet_rq_post(phba, ctxp, &ctxp->rqb_buffer->hbuf);
+               spin_lock_irqsave(&ctxp->ctxlock, flags);
+               if (ctxp->flag & LPFC_NVMET_CTX_RLS)
+                       released = true;
+               ctxp->flag &= ~LPFC_NVMET_ABORT_OP;
+               spin_unlock_irqrestore(&ctxp->ctxlock, flags);
+
+               /*
+                * if transport has released ctx, then can reuse it. Otherwise,
+                * will be recycled by transport release call.
+                */
+               if (released)
+                       lpfc_nvmet_rq_post(phba, ctxp, &ctxp->rqb_buffer->hbuf);
+
                cmdwqe->context2 = NULL;
                cmdwqe->context3 = NULL;
        }
 
        struct lpfc_iocbq *wqeq;
        struct lpfc_iocbq *abort_wqeq;
        dma_addr_t txrdy_phys;
+       spinlock_t ctxlock; /* protect flag access */
        uint32_t *txrdy;
        uint32_t sid;
        uint32_t offset;
 #define LPFC_NVMET_STE_RSP             4
 #define LPFC_NVMET_STE_DONE            5
        uint16_t flag;
-#define LPFC_NVMET_IO_INP              1
-#define LPFC_NVMET_ABORT_OP            2
+#define LPFC_NVMET_IO_INP              0x1
+#define LPFC_NVMET_ABORT_OP            0x2
+#define LPFC_NVMET_CTX_RLS             0x4
+
        struct rqb_dmabuf *rqb_buffer;
 
 #ifdef CONFIG_SCSI_LPFC_DEBUG_FS
 
  *       be freed/released.
  *       Entrypoint is Mandatory.
  *
- * @fcp_op:  Called to perform a data transfer, transmit a response, or
- *       abort an FCP opertion. The nvmefc_tgt_fcp_req structure is the same
- *       LLDD-supplied exchange structure specified in the
- *       nvmet_fc_rcv_fcp_req() call made when the FCP CMD IU was received.
- *       The op field in the structure shall indicate the operation for
- *       the LLDD to perform relative to the io.
+ * @fcp_op:  Called to perform a data transfer or transmit a response.
+ *       The nvmefc_tgt_fcp_req structure is the same LLDD-supplied
+ *       exchange structure specified in the nvmet_fc_rcv_fcp_req() call
+ *       made when the FCP CMD IU was received. The op field in the
+ *       structure shall indicate the operation for the LLDD to perform
+ *       relative to the io.
  *         NVMET_FCOP_READDATA operation: the LLDD is to send the
  *           payload data (described by sglist) to the host in 1 or
  *           more FC sequences (preferrably 1).  Note: the fc-nvme layer
  *           successfully, the LLDD is to update the nvmefc_tgt_fcp_req
  *           transferred_length field and may subsequently transmit the
  *           FCP_RSP iu payload (described by rspbuf, rspdma, rsplen).
- *           The LLDD is to await FCP_CONF reception to confirm the RSP
- *           reception by the host. The LLDD may retramsit the FCP_RSP iu
- *           if necessary per FC-NVME. Upon reception of FCP_CONF, or upon
- *           FCP_CONF failure, the LLDD is to set the nvmefc_tgt_fcp_req
- *           fcp_error field and consider the operation complete..
+ *           If FCP_CONF is supported, the LLDD is to await FCP_CONF
+ *           reception to confirm the RSP reception by the host. The LLDD
+ *           may retramsit the FCP_RSP iu if necessary per FC-NVME. Upon
+ *           transmission of the FCP_RSP iu if FCP_CONF is not supported,
+ *           or upon success/failure of FCP_CONF if it is supported, the
+ *           LLDD is to set the nvmefc_tgt_fcp_req fcp_error field and
+ *           consider the operation complete.
  *         NVMET_FCOP_RSP: the LLDD is to transmit the FCP_RSP iu payload
- *           (described by rspbuf, rspdma, rsplen).  The LLDD is to await
- *           FCP_CONF reception to confirm the RSP reception by the host.
- *           The LLDD may retramsit the FCP_RSP iu if necessary per FC-NVME.
- *           Upon reception of FCP_CONF, or upon FCP_CONF failure, the
+ *           (described by rspbuf, rspdma, rsplen). If FCP_CONF is
+ *           supported, the LLDD is to await FCP_CONF reception to confirm
+ *           the RSP reception by the host. The LLDD may retramsit the
+ *           FCP_RSP iu if FCP_CONF is not received per FC-NVME. Upon
+ *           transmission of the FCP_RSP iu if FCP_CONF is not supported,
+ *           or upon success/failure of FCP_CONF if it is supported, the
  *           LLDD is to set the nvmefc_tgt_fcp_req fcp_error field and
- *           consider the operation complete..
+ *           consider the operation complete.
  *         NVMET_FCOP_ABORT: the LLDD is to terminate the exchange
  *           corresponding to the fcp operation. The LLDD shall send
  *           ABTS and follow FC exchange abort-multi rules, including
  *           ABTS retries and possible logout.
  *       Upon completing the indicated operation, the LLDD is to set the
  *       status fields for the operation (tranferred_length and fcp_error
- *       status) in the request, then all the "done" routine
- *       indicated in the fcp request.  Upon return from the "done"
- *       routine for either a NVMET_FCOP_RSP or NVMET_FCOP_ABORT operation
- *       the fc-nvme layer will not longer reference the fcp request,
- *       allowing the LLDD to free/release the fcp request.
+ *       status) in the request, then call the "done" routine
+ *       indicated in the fcp request. After the operation completes,
+ *       regardless of whether the FCP_RSP iu was successfully transmit,
+ *       the LLDD-supplied exchange structure must remain valid until the
+ *       transport calls the fcp_req_release() callback to return ownership
+ *       of the exchange structure back to the LLDD so that it may be used
+ *       for another fcp command.
  *       Note: when calling the done routine for READDATA or WRITEDATA
  *       operations, the fc-nvme layer may immediate convert, in the same
  *       thread and before returning to the LLDD, the fcp operation to
  *       Returns 0 on success, -<errno> on failure (Ex: -EIO)
  *       Entrypoint is Mandatory.
  *
+ * @fcp_req_release:  Called by the transport to return a nvmefc_tgt_fcp_req
+ *       to the LLDD after all operations on the fcp operation are complete.
+ *       This may be due to the command completing or upon completion of
+ *       abort cleanup.
+ *
  * @max_hw_queues:  indicates the maximum number of hw queues the LLDD
  *       supports for cpu affinitization.
  *       Value is Mandatory. Must be at least 1.
        int (*xmt_ls_rsp)(struct nvmet_fc_target_port *tgtport,
                                struct nvmefc_tgt_ls_req *tls_req);
        int (*fcp_op)(struct nvmet_fc_target_port *tgtport,
-                               struct nvmefc_tgt_fcp_req *);
+                               struct nvmefc_tgt_fcp_req *fcpreq);
+       void (*fcp_req_release)(struct nvmet_fc_target_port *tgtport,
+                               struct nvmefc_tgt_fcp_req *fcpreq);
 
        u32     max_hw_queues;
        u16     max_sgl_segments;