[SCSI] lpfc 8.1.12 : Modify ELS abort handling to prevent double completion

Modify ELS abort handling to prevent double completion

Rework portions of ELS abort handling to prevent double completion
 - Rework ELS iotags and correct abort routine
 - Move the (badly wrong) ELS completion logic from the initial ELS
   abort request function to the ELS completion function.
 - Fixup the iocb completion handling to account for the ELS abort
   completions.

Signed-off-by: James Smart <James.Smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
This commit is contained in:
James Smart 2007-04-25 09:51:38 -04:00 committed by James Bottomley
parent 1dcb58e568
commit 07951076ae
6 changed files with 98 additions and 182 deletions

View file

@ -66,8 +66,7 @@ int lpfc_disc_state_machine(struct lpfc_hba *, struct lpfc_nodelist *, void *,
int lpfc_check_sparm(struct lpfc_hba *, struct lpfc_nodelist *,
struct serv_parm *, uint32_t);
int lpfc_els_abort(struct lpfc_hba *, struct lpfc_nodelist * ndlp,
int);
int lpfc_els_abort(struct lpfc_hba *, struct lpfc_nodelist * ndlp);
int lpfc_els_abort_flogi(struct lpfc_hba *);
int lpfc_initial_flogi(struct lpfc_hba *);
int lpfc_issue_els_plogi(struct lpfc_hba *, uint32_t, uint8_t);
@ -162,8 +161,8 @@ int lpfc_sli_ringpostbuf_put(struct lpfc_hba *, struct lpfc_sli_ring *,
struct lpfc_dmabuf *lpfc_sli_ringpostbuf_get(struct lpfc_hba *,
struct lpfc_sli_ring *,
dma_addr_t);
int lpfc_sli_issue_abort_iotag32(struct lpfc_hba *, struct lpfc_sli_ring *,
struct lpfc_iocbq *);
int lpfc_sli_issue_abort_iotag(struct lpfc_hba *, struct lpfc_sli_ring *,
struct lpfc_iocbq *);
int lpfc_sli_sum_iocb(struct lpfc_hba *, struct lpfc_sli_ring *, uint16_t,
uint64_t, lpfc_ctx_cmd);
int lpfc_sli_abort_iocb(struct lpfc_hba *, struct lpfc_sli_ring *, uint16_t,

View file

@ -582,24 +582,8 @@ lpfc_els_abort_flogi(struct lpfc_hba * phba)
icmd = &iocb->iocb;
if (icmd->ulpCommand == CMD_ELS_REQUEST64_CR) {
ndlp = (struct lpfc_nodelist *)(iocb->context1);
if (ndlp && (ndlp->nlp_DID == Fabric_DID)) {
list_del(&iocb->list);
pring->txcmplq_cnt--;
if ((icmd->un.elsreq64.bdl.ulpIoTag32)) {
lpfc_sli_issue_abort_iotag32
(phba, pring, iocb);
}
if (iocb->iocb_cmpl) {
icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
icmd->un.ulpWord[4] =
IOERR_SLI_ABORTED;
spin_unlock_irq(phba->host->host_lock);
(iocb->iocb_cmpl) (phba, iocb, iocb);
spin_lock_irq(phba->host->host_lock);
} else
lpfc_sli_release_iocbq(phba, iocb);
}
if (ndlp && (ndlp->nlp_DID == Fabric_DID))
lpfc_sli_issue_abort_iotag(phba, pring, iocb);
}
}
spin_unlock_irq(phba->host->host_lock);
@ -3245,7 +3229,6 @@ lpfc_els_timeout_handler(struct lpfc_hba *phba)
struct lpfc_iocbq *tmp_iocb, *piocb;
IOCB_t *cmd = NULL;
struct lpfc_dmabuf *pcmd;
struct list_head *dlp;
uint32_t *elscmd;
uint32_t els_command;
uint32_t timeout;
@ -3262,7 +3245,6 @@ lpfc_els_timeout_handler(struct lpfc_hba *phba)
timeout = (uint32_t)(phba->fc_ratov << 1);
pring = &phba->sli.ring[LPFC_ELS_RING];
dlp = &pring->txcmplq;
list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) {
cmd = &piocb->iocb;
@ -3288,19 +3270,12 @@ lpfc_els_timeout_handler(struct lpfc_hba *phba)
continue;
}
list_del(&piocb->list);
pring->txcmplq_cnt--;
if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) {
struct lpfc_nodelist *ndlp;
spin_unlock_irq(phba->host->host_lock);
ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext);
spin_lock_irq(phba->host->host_lock);
remote_ID = ndlp->nlp_DID;
if (cmd->un.elsreq64.bdl.ulpIoTag32) {
lpfc_sli_issue_abort_iotag32(phba,
pring, piocb);
}
} else {
remote_ID = cmd->un.elsreq64.remoteID;
}
@ -3312,17 +3287,7 @@ lpfc_els_timeout_handler(struct lpfc_hba *phba)
phba->brd_no, els_command,
remote_ID, cmd->ulpCommand, cmd->ulpIoTag);
/*
* The iocb has timed out; abort it.
*/
if (piocb->iocb_cmpl) {
cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
spin_unlock_irq(phba->host->host_lock);
(piocb->iocb_cmpl) (phba, piocb, piocb);
spin_lock_irq(phba->host->host_lock);
} else
lpfc_sli_release_iocbq(phba, piocb);
lpfc_sli_issue_abort_iotag(phba, pring, piocb);
}
if (phba->sli.ring[LPFC_ELS_RING].txcmplq_cnt)
mod_timer(&phba->els_tmofunc, jiffies + HZ * timeout);
@ -3336,9 +3301,6 @@ lpfc_els_flush_cmd(struct lpfc_hba * phba)
struct lpfc_sli_ring *pring;
struct lpfc_iocbq *tmp_iocb, *piocb;
IOCB_t *cmd = NULL;
struct lpfc_dmabuf *pcmd;
uint32_t *elscmd;
uint32_t els_command;
pring = &phba->sli.ring[LPFC_ELS_RING];
spin_lock_irq(phba->host->host_lock);
@ -3357,10 +3319,6 @@ lpfc_els_flush_cmd(struct lpfc_hba * phba)
continue;
}
pcmd = (struct lpfc_dmabuf *) piocb->context2;
elscmd = (uint32_t *) (pcmd->virt);
els_command = *elscmd;
list_del(&piocb->list);
pring->txq_cnt--;
@ -3381,22 +3339,8 @@ lpfc_els_flush_cmd(struct lpfc_hba * phba)
if (piocb->iocb_flag & LPFC_IO_LIBDFC) {
continue;
}
pcmd = (struct lpfc_dmabuf *) piocb->context2;
elscmd = (uint32_t *) (pcmd->virt);
els_command = *elscmd;
list_del(&piocb->list);
pring->txcmplq_cnt--;
cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
if (piocb->iocb_cmpl) {
spin_unlock_irq(phba->host->host_lock);
(piocb->iocb_cmpl) (phba, piocb, piocb);
spin_lock_irq(phba->host->host_lock);
} else
lpfc_sli_release_iocbq(phba, piocb);
lpfc_sli_issue_abort_iotag(phba, pring, piocb);
}
spin_unlock_irq(phba->host->host_lock);
return;

View file

@ -1596,7 +1596,7 @@ lpfc_freenode(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
}
spin_unlock_irq(phba->host->host_lock);
lpfc_els_abort(phba,ndlp,0);
lpfc_els_abort(phba,ndlp);
spin_lock_irq(phba->host->host_lock);
ndlp->nlp_flag &= ~NLP_DELAY_TMO;
spin_unlock_irq(phba->host->host_lock);

View file

@ -168,8 +168,7 @@ lpfc_check_elscmpl_iocb(struct lpfc_hba * phba,
* routine effectively results in a "software abort".
*/
int
lpfc_els_abort(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
int send_abts)
lpfc_els_abort(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
{
struct lpfc_sli *psli;
struct lpfc_sli_ring *pring;
@ -215,48 +214,17 @@ lpfc_els_abort(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
spin_unlock_irq(phba->host->host_lock);
} while (found);
/* Everything on txcmplq will be returned by firmware
* with a no rpi / linkdown / abort error. For ring 0,
* ELS discovery, we want to get rid of it right here.
*/
/* Next check the txcmplq */
do {
found = 0;
spin_lock_irq(phba->host->host_lock);
list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq,
list) {
/* Check to see if iocb matches the nport we are looking
for */
if ((lpfc_check_sli_ndlp (phba, pring, iocb, ndlp))) {
found = 1;
/* It matches, so deque and call compl with an
error */
list_del(&iocb->list);
pring->txcmplq_cnt--;
icmd = &iocb->iocb;
/* If the driver is completing an ELS
* command early, flush it out of the firmware.
*/
if (send_abts &&
(icmd->ulpCommand == CMD_ELS_REQUEST64_CR) &&
(icmd->un.elsreq64.bdl.ulpIoTag32)) {
lpfc_sli_issue_abort_iotag32(phba,
pring, iocb);
}
if (iocb->iocb_cmpl) {
icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
spin_unlock_irq(phba->host->host_lock);
(iocb->iocb_cmpl) (phba, iocb, iocb);
spin_lock_irq(phba->host->host_lock);
} else
lpfc_sli_release_iocbq(phba, iocb);
break;
}
spin_lock_irq(phba->host->host_lock);
list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
/* Check to see if iocb matches the nport we are looking
for */
if ((lpfc_check_sli_ndlp (phba, pring, iocb, ndlp))) {
icmd = &iocb->iocb;
lpfc_sli_issue_abort_iotag(phba, pring, iocb);
}
spin_unlock_irq(phba->host->host_lock);
} while(found);
}
spin_unlock_irq(phba->host->host_lock);
/* If we are delaying issuing an ELS command, cancel it */
if (ndlp->nlp_flag & NLP_DELAY_TMO)
@ -404,7 +372,7 @@ lpfc_rcv_plogi(struct lpfc_hba * phba,
*/
if (ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) {
/* software abort outstanding PLOGI */
lpfc_els_abort(phba, ndlp, 1);
lpfc_els_abort(phba, ndlp);
}
lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox, 0);
@ -697,7 +665,7 @@ lpfc_rcv_logo_plogi_issue(struct lpfc_hba * phba,
cmdiocb = (struct lpfc_iocbq *) arg;
/* software abort outstanding PLOGI */
lpfc_els_abort(phba, ndlp, 1);
lpfc_els_abort(phba, ndlp);
lpfc_rcv_logo(phba, ndlp, cmdiocb, ELS_CMD_LOGO);
return ndlp->nlp_state;
@ -712,7 +680,7 @@ lpfc_rcv_els_plogi_issue(struct lpfc_hba * phba,
cmdiocb = (struct lpfc_iocbq *) arg;
/* software abort outstanding PLOGI */
lpfc_els_abort(phba, ndlp, 1);
lpfc_els_abort(phba, ndlp);
if (evt == NLP_EVT_RCV_LOGO) {
lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
@ -855,7 +823,7 @@ lpfc_device_rm_plogi_issue(struct lpfc_hba * phba,
}
else {
/* software abort outstanding PLOGI */
lpfc_els_abort(phba, ndlp, 1);
lpfc_els_abort(phba, ndlp);
lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
return NLP_STE_FREED_NODE;
@ -868,7 +836,7 @@ lpfc_device_recov_plogi_issue(struct lpfc_hba * phba,
uint32_t evt)
{
/* software abort outstanding PLOGI */
lpfc_els_abort(phba, ndlp, 1);
lpfc_els_abort(phba, ndlp);
ndlp->nlp_prev_state = NLP_STE_PLOGI_ISSUE;
ndlp->nlp_state = NLP_STE_NPR_NODE;
@ -888,7 +856,7 @@ lpfc_rcv_plogi_adisc_issue(struct lpfc_hba * phba,
struct lpfc_iocbq *cmdiocb;
/* software abort outstanding ADISC */
lpfc_els_abort(phba, ndlp, 1);
lpfc_els_abort(phba, ndlp);
cmdiocb = (struct lpfc_iocbq *) arg;
@ -926,7 +894,7 @@ lpfc_rcv_logo_adisc_issue(struct lpfc_hba * phba,
cmdiocb = (struct lpfc_iocbq *) arg;
/* software abort outstanding ADISC */
lpfc_els_abort(phba, ndlp, 0);
lpfc_els_abort(phba, ndlp);
lpfc_rcv_logo(phba, ndlp, cmdiocb, ELS_CMD_LOGO);
return ndlp->nlp_state;
@ -1016,7 +984,7 @@ lpfc_device_rm_adisc_issue(struct lpfc_hba * phba,
}
else {
/* software abort outstanding ADISC */
lpfc_els_abort(phba, ndlp, 1);
lpfc_els_abort(phba, ndlp);
lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
return NLP_STE_FREED_NODE;
@ -1029,7 +997,7 @@ lpfc_device_recov_adisc_issue(struct lpfc_hba * phba,
uint32_t evt)
{
/* software abort outstanding ADISC */
lpfc_els_abort(phba, ndlp, 1);
lpfc_els_abort(phba, ndlp);
ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
ndlp->nlp_state = NLP_STE_NPR_NODE;
@ -1230,7 +1198,7 @@ lpfc_rcv_logo_prli_issue(struct lpfc_hba * phba,
cmdiocb = (struct lpfc_iocbq *) arg;
/* Software abort outstanding PRLI before sending acc */
lpfc_els_abort(phba, ndlp, 1);
lpfc_els_abort(phba, ndlp);
lpfc_rcv_logo(phba, ndlp, cmdiocb, ELS_CMD_LOGO);
return ndlp->nlp_state;
@ -1330,7 +1298,7 @@ lpfc_device_rm_prli_issue(struct lpfc_hba * phba,
}
else {
/* software abort outstanding PLOGI */
lpfc_els_abort(phba, ndlp, 1);
lpfc_els_abort(phba, ndlp);
lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
return NLP_STE_FREED_NODE;
@ -1359,7 +1327,7 @@ lpfc_device_recov_prli_issue(struct lpfc_hba * phba,
struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
{
/* software abort outstanding PRLI */
lpfc_els_abort(phba, ndlp, 1);
lpfc_els_abort(phba, ndlp);
ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE;
ndlp->nlp_state = NLP_STE_NPR_NODE;

View file

@ -816,6 +816,14 @@ lpfc_sli_process_sol_iocb(struct lpfc_hba * phba, struct lpfc_sli_ring * pring,
* All other are passed to the completion callback.
*/
if (pring->ringno == LPFC_ELS_RING) {
if (cmdiocbp->iocb_flag & LPFC_DRIVER_ABORTED) {
cmdiocbp->iocb_flag &=
~LPFC_DRIVER_ABORTED;
saveq->iocb.ulpStatus =
IOSTAT_LOCAL_REJECT;
saveq->iocb.un.ulpWord[4] =
IOERR_SLI_ABORTED;
}
spin_unlock_irqrestore(phba->host->host_lock,
iflag);
(cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq);
@ -2728,85 +2736,81 @@ lpfc_sli_ringpostbuf_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
static void
lpfc_sli_abort_elsreq_cmpl(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
struct lpfc_iocbq * rspiocb)
lpfc_sli_abort_els_cmpl(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
struct lpfc_iocbq * rspiocb)
{
struct lpfc_dmabuf *buf_ptr, *buf_ptr1;
/* Free the resources associated with the ELS_REQUEST64 IOCB the driver
* just aborted.
* In this case, context2 = cmd, context2->next = rsp, context3 = bpl
*/
if (cmdiocb->context2) {
buf_ptr1 = (struct lpfc_dmabuf *) cmdiocb->context2;
/* Free the response IOCB before completing the abort
command. */
buf_ptr = NULL;
list_remove_head((&buf_ptr1->list), buf_ptr,
struct lpfc_dmabuf, list);
if (buf_ptr) {
lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
kfree(buf_ptr);
}
lpfc_mbuf_free(phba, buf_ptr1->virt, buf_ptr1->phys);
kfree(buf_ptr1);
}
if (cmdiocb->context3) {
buf_ptr = (struct lpfc_dmabuf *) cmdiocb->context3;
lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
kfree(buf_ptr);
}
spin_lock_irq(phba->host->host_lock);
lpfc_sli_release_iocbq(phba, cmdiocb);
spin_unlock_irq(phba->host->host_lock);
return;
}
int
lpfc_sli_issue_abort_iotag32(struct lpfc_hba * phba,
struct lpfc_sli_ring * pring,
struct lpfc_iocbq * cmdiocb)
lpfc_sli_issue_abort_iotag(struct lpfc_hba * phba,
struct lpfc_sli_ring * pring,
struct lpfc_iocbq * cmdiocb)
{
struct lpfc_iocbq *abtsiocbp;
IOCB_t *icmd = NULL;
IOCB_t *iabt = NULL;
int retval = IOCB_ERROR;
/* There are certain command types we don't want
* to abort.
*/
icmd = &cmdiocb->iocb;
if ((icmd->ulpCommand == CMD_ABORT_XRI_CN) ||
(icmd->ulpCommand == CMD_CLOSE_XRI_CN))
return 0;
/* If we're unloading, interrupts are disabled so we
* need to cleanup the iocb here.
*/
if (phba->fc_flag & FC_UNLOADING)
goto abort_iotag_exit;
/* issue ABTS for this IOCB based on iotag */
abtsiocbp = lpfc_sli_get_iocbq(phba);
if (abtsiocbp == NULL)
return 0;
/* This signals the response to set the correct status
* before calling the completion handler.
*/
cmdiocb->iocb_flag |= LPFC_DRIVER_ABORTED;
iabt = &abtsiocbp->iocb;
icmd = &cmdiocb->iocb;
switch (icmd->ulpCommand) {
case CMD_ELS_REQUEST64_CR:
/* Even though we abort the ELS command, the firmware may access
* the BPL or other resources before it processes our
* ABORT_MXRI64. Thus we must delay reusing the cmdiocb
* resources till the actual abort request completes.
*/
abtsiocbp->context1 = (void *)((unsigned long)icmd->ulpCommand);
abtsiocbp->context2 = cmdiocb->context2;
abtsiocbp->context3 = cmdiocb->context3;
cmdiocb->context2 = NULL;
cmdiocb->context3 = NULL;
abtsiocbp->iocb_cmpl = lpfc_sli_abort_elsreq_cmpl;
break;
default:
lpfc_sli_release_iocbq(phba, abtsiocbp);
return 0;
}
iabt->un.amxri.abortType = ABORT_TYPE_ABTS;
iabt->un.amxri.iotag32 = icmd->un.elsreq64.bdl.ulpIoTag32;
iabt->un.acxri.abortType = ABORT_TYPE_ABTS;
iabt->un.acxri.abortContextTag = icmd->ulpContext;
iabt->un.acxri.abortIoTag = icmd->ulpIoTag;
iabt->ulpLe = 1;
iabt->ulpClass = CLASS3;
iabt->ulpCommand = CMD_ABORT_MXRI64_CN;
iabt->ulpClass = icmd->ulpClass;
if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == IOCB_ERROR) {
lpfc_sli_release_iocbq(phba, abtsiocbp);
return 0;
if (phba->hba_state >= LPFC_LINK_UP)
iabt->ulpCommand = CMD_ABORT_XRI_CN;
else
iabt->ulpCommand = CMD_CLOSE_XRI_CN;
abtsiocbp->iocb_cmpl = lpfc_sli_abort_els_cmpl;
retval = lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0);
abort_iotag_exit:
/* If we could not issue an abort dequeue the iocb and handle
* the completion here.
*/
if (retval == IOCB_ERROR) {
list_del(&cmdiocb->list);
pring->txcmplq_cnt--;
if (cmdiocb->iocb_cmpl) {
icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
spin_unlock_irq(phba->host->host_lock);
(cmdiocb->iocb_cmpl) (phba, cmdiocb, cmdiocb);
spin_lock_irq(phba->host->host_lock);
} else
lpfc_sli_release_iocbq(phba, cmdiocb);
}
return 1;

View file

@ -39,9 +39,10 @@ struct lpfc_iocbq {
IOCB_t iocb; /* IOCB cmd */
uint8_t retry; /* retry counter for IOCB cmd - if needed */
uint8_t iocb_flag;
#define LPFC_IO_LIBDFC 1 /* libdfc iocb */
#define LPFC_IO_WAKE 2 /* High Priority Queue signal flag */
#define LPFC_IO_FCP 4 /* FCP command -- iocbq in scsi_buf */
#define LPFC_IO_LIBDFC 1 /* libdfc iocb */
#define LPFC_IO_WAKE 2 /* High Priority Queue signal flag */
#define LPFC_IO_FCP 4 /* FCP command -- iocbq in scsi_buf */
#define LPFC_DRIVER_ABORTED 8 /* driver aborted this request */
uint8_t abort_count;
uint8_t rsvd2;