Skip to content

Commit

Permalink
scsi: lpfc: Rework locking on SCSI io completion
Browse files Browse the repository at this point in the history
A scsi host lock is taken on every io completion to check whether the abort
handler is waiting on the io completion. This is an expensive lock to take
on all completion when rarely in an abort condition.

Replace scsi host lock with command-specific lock. Synchronize completion
and abort paths by new cmd lock. Ensure all flag changing and nulling of
context pointers taken under lock.  When adding lock to task management
abort, realized it was missing other synchronization locks. Added that
synchronization to match normal paths.

Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>
Reviewed-by: Hannes Reinecke <hare@suse.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
  • Loading branch information
James Smart authored and Martin K. Petersen committed Feb 6, 2019
1 parent b1684a0 commit c201726
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 74 deletions.
1 change: 1 addition & 0 deletions drivers/scsi/lpfc/lpfc_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -4160,6 +4160,7 @@ lpfc_new_io_buf(struct lpfc_hba *phba, int num_to_alloc)
lpfc_ncmd->dma_sgl = lpfc_ncmd->data;
lpfc_ncmd->dma_phys_sgl = lpfc_ncmd->dma_handle;
lpfc_ncmd->cur_iocbq.context1 = lpfc_ncmd;
spin_lock_init(&lpfc_ncmd->buf_lock);

/* add the nvme buffer to a post list */
list_add_tail(&lpfc_ncmd->list, &post_nblist);
Expand Down
48 changes: 30 additions & 18 deletions drivers/scsi/lpfc/lpfc_nvme.c
Original file line number Diff line number Diff line change
Expand Up @@ -969,15 +969,19 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn,
uint32_t *ptr;

/* Sanity check on return of outstanding command */
if (!lpfc_ncmd || !lpfc_ncmd->nvmeCmd) {
if (!lpfc_ncmd) {
lpfc_printf_vlog(vport, KERN_ERR,
LOG_NODE | LOG_NVME_IOERR,
"6071 Null lpfc_ncmd pointer. No "
"release, skip completion\n");
return;
}
if (!lpfc_ncmd) {
lpfc_printf_vlog(vport, KERN_ERR,
LOG_NODE | LOG_NVME_IOERR,
"6071 Null lpfc_ncmd pointer. No "
"release, skip completion\n");
return;
}

/* Guard against abort handler being called at same time */
spin_lock(&lpfc_ncmd->buf_lock);

if (!lpfc_ncmd->nvmeCmd) {
spin_unlock(&lpfc_ncmd->buf_lock);
lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR,
"6066 Missing cmpl ptrs: lpfc_ncmd %p, "
"nvmeCmd %p\n",
Expand Down Expand Up @@ -1154,9 +1158,11 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn,
if (!(lpfc_ncmd->flags & LPFC_SBUF_XBUSY)) {
freqpriv = nCmd->private;
freqpriv->nvme_buf = NULL;
nCmd->done(nCmd);
lpfc_ncmd->nvmeCmd = NULL;
}
spin_unlock(&lpfc_ncmd->buf_lock);
nCmd->done(nCmd);
} else
spin_unlock(&lpfc_ncmd->buf_lock);

/* Call release with XB=1 to queue the IO into the abort list. */
lpfc_release_nvme_buf(phba, lpfc_ncmd);
Expand Down Expand Up @@ -1781,6 +1787,9 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
}
nvmereq_wqe = &lpfc_nbuf->cur_iocbq;

/* Guard against IO completion being called at same time */
spin_lock(&lpfc_nbuf->buf_lock);

/*
* The lpfc_nbuf and the mapped nvme_fcreq in the driver's
* state must match the nvme_fcreq passed by the nvme
Expand All @@ -1789,24 +1798,22 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
* has not seen it yet.
*/
if (lpfc_nbuf->nvmeCmd != pnvme_fcreq) {
spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS,
"6143 NVME req mismatch: "
"lpfc_nbuf %p nvmeCmd %p, "
"pnvme_fcreq %p. Skipping Abort xri x%x\n",
lpfc_nbuf, lpfc_nbuf->nvmeCmd,
pnvme_fcreq, nvmereq_wqe->sli4_xritag);
return;
goto out_unlock;
}

/* Don't abort IOs no longer on the pending queue. */
if (!(nvmereq_wqe->iocb_flag & LPFC_IO_ON_TXCMPLQ)) {
spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS,
"6142 NVME IO req %p not queued - skipping "
"abort req xri x%x\n",
pnvme_fcreq, nvmereq_wqe->sli4_xritag);
return;
goto out_unlock;
}

atomic_inc(&lport->xmt_fcp_abort);
Expand All @@ -1816,24 +1823,22 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,

/* Outstanding abort is in progress */
if (nvmereq_wqe->iocb_flag & LPFC_DRIVER_ABORTED) {
spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS,
"6144 Outstanding NVME I/O Abort Request "
"still pending on nvme_fcreq %p, "
"lpfc_ncmd %p xri x%x\n",
pnvme_fcreq, lpfc_nbuf,
nvmereq_wqe->sli4_xritag);
return;
goto out_unlock;
}

abts_buf = __lpfc_sli_get_iocbq(phba);
if (!abts_buf) {
spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS,
"6136 No available abort wqes. Skipping "
"Abts req for nvme_fcreq %p xri x%x\n",
pnvme_fcreq, nvmereq_wqe->sli4_xritag);
return;
goto out_unlock;
}

/* Ready - mark outstanding as aborted by driver. */
Expand Down Expand Up @@ -1877,6 +1882,7 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
abts_buf->vport = vport;
abts_buf->wqe_cmpl = lpfc_nvme_abort_fcreq_cmpl;
ret_val = lpfc_sli4_issue_wqe(phba, lpfc_nbuf->hdwq, abts_buf);
spin_unlock(&lpfc_nbuf->buf_lock);
spin_unlock_irqrestore(&phba->hbalock, flags);
if (ret_val) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS,
Expand All @@ -1892,6 +1898,12 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport,
"ox_id x%x on reqtag x%x\n",
nvmereq_wqe->sli4_xritag,
abts_buf->iotag);
return;

out_unlock:
spin_unlock(&lpfc_nbuf->buf_lock);
spin_unlock_irqrestore(&phba->hbalock, flags);
return;
}

/* Declare and initialization an instance of the FC NVME template. */
Expand Down
94 changes: 47 additions & 47 deletions drivers/scsi/lpfc/lpfc_scsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
psb->status = IOSTAT_SUCCESS;
/* Put it back into the SCSI buffer list */
psb->cur_iocbq.context1 = psb;
spin_lock_init(&psb->buf_lock);
lpfc_release_scsi_buf_s3(phba, psb);

}
Expand Down Expand Up @@ -712,7 +713,6 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
lpfc_cmd->cur_iocbq.iocb_flag = LPFC_IO_FCP;
lpfc_cmd->prot_seg_cnt = 0;
lpfc_cmd->seg_cnt = 0;
lpfc_cmd->waitq = NULL;
lpfc_cmd->timeout = 0;
lpfc_cmd->flags = 0;
lpfc_cmd->start_time = jiffies;
Expand Down Expand Up @@ -3651,10 +3651,17 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
int cpu;
#endif

/* Guard against abort handler being called at same time */
spin_lock(&lpfc_cmd->buf_lock);

/* Sanity check on return of outstanding command */
cmd = lpfc_cmd->pCmd;
if (!cmd)
if (!cmd) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
"2621 IO completion: Not an active IO\n");
spin_unlock(&lpfc_cmd->buf_lock);
return;
}

idx = lpfc_cmd->cur_iocbq.hba_wqidx;
if (phba->sli4_hba.hdwq)
Expand Down Expand Up @@ -3860,29 +3867,24 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
}
lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);

/* If pCmd was set to NULL from abort path, do not call scsi_done */
if (xchg(&lpfc_cmd->pCmd, NULL) == NULL) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
"5688 FCP cmd already NULL, sid: 0x%06x, "
"did: 0x%06x, oxid: 0x%04x\n",
vport->fc_myDID,
(pnode) ? pnode->nlp_DID : 0,
phba->sli_rev == LPFC_SLI_REV4 ?
lpfc_cmd->cur_iocbq.sli4_xritag : 0xffff);
return;
}
lpfc_cmd->pCmd = NULL;
spin_unlock(&lpfc_cmd->buf_lock);

/* The sdev is not guaranteed to be valid post scsi_done upcall. */
cmd->scsi_done(cmd);

/*
* If there is a thread waiting for command completion
* If there is an abort thread waiting for command completion
* wake up the thread.
*/
spin_lock_irqsave(shost->host_lock, flags);
if (lpfc_cmd->waitq)
wake_up(lpfc_cmd->waitq);
spin_unlock_irqrestore(shost->host_lock, flags);
spin_lock(&lpfc_cmd->buf_lock);
if (unlikely(lpfc_cmd->cur_iocbq.iocb_flag & LPFC_DRIVER_ABORTED)) {
lpfc_cmd->cur_iocbq.iocb_flag &= ~LPFC_DRIVER_ABORTED;
if (lpfc_cmd->waitq)
wake_up(lpfc_cmd->waitq);
lpfc_cmd->waitq = NULL;
}
spin_unlock(&lpfc_cmd->buf_lock);

lpfc_release_scsi_buf(phba, lpfc_cmd);
}
Expand Down Expand Up @@ -4563,44 +4565,47 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
if (status != 0 && status != SUCCESS)
return status;

lpfc_cmd = (struct lpfc_io_buf *)cmnd->host_scribble;
if (!lpfc_cmd)
return ret;

spin_lock_irqsave(&phba->hbalock, flags);
/* driver queued commands are in process of being flushed */
if (phba->hba_flag & HBA_FCP_IOQ_FLUSH) {
spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"3168 SCSI Layer abort requested I/O has been "
"flushed by LLD.\n");
return FAILED;
ret = FAILED;
goto out_unlock;
}

lpfc_cmd = (struct lpfc_io_buf *)cmnd->host_scribble;
if (!lpfc_cmd || !lpfc_cmd->pCmd) {
spin_unlock_irqrestore(&phba->hbalock, flags);
/* Guard against IO completion being called at same time */
spin_lock(&lpfc_cmd->buf_lock);

if (!lpfc_cmd->pCmd) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"2873 SCSI Layer I/O Abort Request IO CMPL Status "
"x%x ID %d LUN %llu\n",
SUCCESS, cmnd->device->id, cmnd->device->lun);
return SUCCESS;
goto out_unlock_buf;
}

iocb = &lpfc_cmd->cur_iocbq;
if (phba->sli_rev == LPFC_SLI_REV4) {
pring_s4 = phba->sli4_hba.hdwq[iocb->hba_wqidx].fcp_wq->pring;
if (!pring_s4) {
ret = FAILED;
goto out_unlock;
goto out_unlock_buf;
}
spin_lock(&pring_s4->ring_lock);
}
/* the command is in process of being cancelled */
if (!(iocb->iocb_flag & LPFC_IO_ON_TXCMPLQ)) {
if (phba->sli_rev == LPFC_SLI_REV4)
spin_unlock(&pring_s4->ring_lock);
spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"3169 SCSI Layer abort requested I/O has been "
"cancelled by LLD.\n");
return FAILED;
ret = FAILED;
goto out_unlock_ring;
}
/*
* If pCmd field of the corresponding lpfc_io_buf structure
Expand All @@ -4609,12 +4614,10 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
* see the completion before the eh fired. Just return SUCCESS.
*/
if (lpfc_cmd->pCmd != cmnd) {
if (phba->sli_rev == LPFC_SLI_REV4)
spin_unlock(&pring_s4->ring_lock);
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"3170 SCSI Layer abort requested I/O has been "
"completed by LLD.\n");
goto out_unlock;
goto out_unlock_ring;
}

BUG_ON(iocb->context1 != lpfc_cmd);
Expand All @@ -4625,16 +4628,15 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
"3389 SCSI Layer I/O Abort Request is pending\n");
if (phba->sli_rev == LPFC_SLI_REV4)
spin_unlock(&pring_s4->ring_lock);
spin_unlock(&lpfc_cmd->buf_lock);
spin_unlock_irqrestore(&phba->hbalock, flags);
goto wait_for_cmpl;
}

abtsiocb = __lpfc_sli_get_iocbq(phba);
if (abtsiocb == NULL) {
ret = FAILED;
if (phba->sli_rev == LPFC_SLI_REV4)
spin_unlock(&pring_s4->ring_lock);
goto out_unlock;
goto out_unlock_ring;
}

/* Indicate the IO is being aborted by the driver. */
Expand Down Expand Up @@ -4684,24 +4686,18 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
/* no longer need the lock after this point */
spin_unlock_irqrestore(&phba->hbalock, flags);


if (ret_val == IOCB_ERROR) {
if (phba->sli_rev == LPFC_SLI_REV4)
spin_lock_irqsave(&pring_s4->ring_lock, flags);
else
spin_lock_irqsave(&phba->hbalock, flags);
/* Indicate the IO is not being aborted by the driver. */
iocb->iocb_flag &= ~LPFC_DRIVER_ABORTED;
lpfc_cmd->waitq = NULL;
if (phba->sli_rev == LPFC_SLI_REV4)
spin_unlock_irqrestore(&pring_s4->ring_lock, flags);
else
spin_unlock_irqrestore(&phba->hbalock, flags);
spin_unlock(&lpfc_cmd->buf_lock);
lpfc_sli_release_iocbq(phba, abtsiocb);
ret = FAILED;
goto out;
}

spin_unlock(&lpfc_cmd->buf_lock);

if (phba->cfg_poll & DISABLE_FCP_RING_INT)
lpfc_sli_handle_fast_ring_event(phba,
&phba->sli.sli3_ring[LPFC_FCP_RING], HA_R0RE_REQ);
Expand All @@ -4712,9 +4708,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
(lpfc_cmd->pCmd != cmnd),
msecs_to_jiffies(2*vport->cfg_devloss_tmo*1000));

spin_lock_irqsave(shost->host_lock, flags);
lpfc_cmd->waitq = NULL;
spin_unlock_irqrestore(shost->host_lock, flags);
spin_lock(&lpfc_cmd->buf_lock);

if (lpfc_cmd->pCmd == cmnd) {
ret = FAILED;
Expand All @@ -4725,8 +4719,14 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
iocb->sli4_xritag, ret,
cmnd->device->id, cmnd->device->lun);
}
spin_unlock(&lpfc_cmd->buf_lock);
goto out;

out_unlock_ring:
if (phba->sli_rev == LPFC_SLI_REV4)
spin_unlock(&pring_s4->ring_lock);
out_unlock_buf:
spin_unlock(&lpfc_cmd->buf_lock);
out_unlock:
spin_unlock_irqrestore(&phba->hbalock, flags);
out:
Expand Down
Loading

0 comments on commit c201726

Please sign in to comment.