Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 07951076 authored by James Smart's avatar James Smart Committed by James Bottomley
Browse files

[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: default avatarJames Smart <James.Smart@emulex.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 1dcb58e5
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -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,7 +161,7 @@ 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 *,
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);
+4 −60
Original line number Diff line number Diff line
@@ -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;
+1 −1
Original line number Diff line number Diff line
@@ -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);
+22 −54
Original line number Diff line number Diff line
@@ -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) {
	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;
			lpfc_sli_issue_abort_iotag(phba, pring, iocb);
		}
	}
	spin_unlock_irq(phba->host->host_lock);
	} while(found);

	/* 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;
+63 −59
Original line number Diff line number Diff line
@@ -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,
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,
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.acxri.abortType = ABORT_TYPE_ABTS;
	iabt->un.acxri.abortContextTag = icmd->ulpContext;
	iabt->un.acxri.abortIoTag = icmd->ulpIoTag;
	iabt->ulpLe = 1;
	iabt->ulpClass = icmd->ulpClass;

	iabt->un.amxri.abortType = ABORT_TYPE_ABTS;
	iabt->un.amxri.iotag32 = icmd->un.elsreq64.bdl.ulpIoTag32;
	if (phba->hba_state >= LPFC_LINK_UP)
		iabt->ulpCommand = CMD_ABORT_XRI_CN;
	else
		iabt->ulpCommand = CMD_CLOSE_XRI_CN;

	iabt->ulpLe = 1;
	iabt->ulpClass = CLASS3;
	iabt->ulpCommand = CMD_ABORT_MXRI64_CN;
	abtsiocbp->iocb_cmpl = lpfc_sli_abort_els_cmpl;
	retval = lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0);

	if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == IOCB_ERROR) {
		lpfc_sli_release_iocbq(phba, abtsiocbp);
		return 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;
Loading