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

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

[SCSI] lpfc 8.3.37: Fixed no-context ABTS failed with BA_RJT



Fixed no-context ABTS received on unsolicited receive queue failed with BA_RJT

Signed-off-by: default avatarJames Smart <james.smart@emulex.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 7b15db32
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -466,11 +466,13 @@ enum intr_type_t {
	MSIX,
};

#define LPFC_CT_CTX_MAX		64
struct unsol_rcv_ct_ctx {
	uint32_t ctxt_id;
	uint32_t SID;
	uint32_t flags;
#define UNSOL_VALID	0x00000001
	uint32_t valid;
#define UNSOL_INVALID		0
#define UNSOL_VALID		1
	uint16_t oxid;
	uint16_t rxid;
};
@@ -938,7 +940,7 @@ struct lpfc_hba {

	spinlock_t ct_ev_lock; /* synchronize access to ct_ev_waiters */
	struct list_head ct_ev_waiters;
	struct unsol_rcv_ct_ctx ct_ctx[64];
	struct unsol_rcv_ct_ctx ct_ctx[LPFC_CT_CTX_MAX];
	uint32_t ctx_idx;

	uint8_t menlo_flag;	/* menlo generic flags */
+46 −5
Original line number Diff line number Diff line
@@ -955,9 +955,9 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
		spin_lock_irqsave(&phba->ct_ev_lock, flags);
		if (phba->sli_rev == LPFC_SLI_REV4) {
			evt_dat->immed_dat = phba->ctx_idx;
			phba->ctx_idx = (phba->ctx_idx + 1) % 64;
			phba->ctx_idx = (phba->ctx_idx + 1) % LPFC_CT_CTX_MAX;
			/* Provide warning for over-run of the ct_ctx array */
			if (phba->ct_ctx[evt_dat->immed_dat].flags &
			if (phba->ct_ctx[evt_dat->immed_dat].valid ==
			    UNSOL_VALID)
				lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
						"2717 CT context array entry "
@@ -973,7 +973,7 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
				piocbq->iocb.unsli3.rcvsli3.ox_id;
			phba->ct_ctx[evt_dat->immed_dat].SID =
				piocbq->iocb.un.rcvels.remoteID;
			phba->ct_ctx[evt_dat->immed_dat].flags = UNSOL_VALID;
			phba->ct_ctx[evt_dat->immed_dat].valid = UNSOL_VALID;
		} else
			evt_dat->immed_dat = piocbq->iocb.ulpContext;

@@ -1012,6 +1012,47 @@ error_ct_unsol_exit:
	return 1;
}

/**
 * lpfc_bsg_ct_unsol_abort - handler ct abort to management plane
 * @phba: Pointer to HBA context object.
 * @dmabuf: pointer to a dmabuf that describes the FC sequence
 *
 * This function handles abort to the CT command toward management plane
 * for SLI4 port.
 *
 * If the pending context of a CT command to management plane present, clears
 * such context and returns 1 for handled; otherwise, it returns 0 indicating
 * no context exists.
 **/
int
lpfc_bsg_ct_unsol_abort(struct lpfc_hba *phba, struct hbq_dmabuf *dmabuf)
{
	struct fc_frame_header fc_hdr;
	struct fc_frame_header *fc_hdr_ptr = &fc_hdr;
	int ctx_idx, handled = 0;
	uint16_t oxid, rxid;
	uint32_t sid;

	memcpy(fc_hdr_ptr, dmabuf->hbuf.virt, sizeof(struct fc_frame_header));
	sid = sli4_sid_from_fc_hdr(fc_hdr_ptr);
	oxid = be16_to_cpu(fc_hdr_ptr->fh_ox_id);
	rxid = be16_to_cpu(fc_hdr_ptr->fh_rx_id);

	for (ctx_idx = 0; ctx_idx < LPFC_CT_CTX_MAX; ctx_idx++) {
		if (phba->ct_ctx[ctx_idx].valid != UNSOL_VALID)
			continue;
		if (phba->ct_ctx[ctx_idx].rxid != rxid)
			continue;
		if (phba->ct_ctx[ctx_idx].oxid != oxid)
			continue;
		if (phba->ct_ctx[ctx_idx].SID != sid)
			continue;
		phba->ct_ctx[ctx_idx].valid = UNSOL_INVALID;
		handled = 1;
	}
	return handled;
}

/**
 * lpfc_bsg_hba_set_event - process a SET_EVENT bsg vendor command
 * @job: SET_EVENT fc_bsg_job
@@ -1318,7 +1359,7 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag,
	icmd->ulpClass = CLASS3;
	if (phba->sli_rev == LPFC_SLI_REV4) {
		/* Do not issue unsol response if oxid not marked as valid */
		if (!(phba->ct_ctx[tag].flags & UNSOL_VALID)) {
		if (phba->ct_ctx[tag].valid != UNSOL_VALID) {
			rc = IOCB_ERROR;
			goto issue_ct_rsp_exit;
		}
@@ -1352,7 +1393,7 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag,
				phba->sli4_hba.rpi_ids[ndlp->nlp_rpi];

		/* The exchange is done, mark the entry as invalid */
		phba->ct_ctx[tag].flags &= ~UNSOL_VALID;
		phba->ct_ctx[tag].valid = UNSOL_INVALID;
	} else
		icmd->ulpContext = (ushort) tag;

+2 −2
Original line number Diff line number Diff line
@@ -164,8 +164,7 @@ void lpfc_hb_timeout_handler(struct lpfc_hba *);

void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
			 struct lpfc_iocbq *);
void lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
				    struct lpfc_iocbq *);
int lpfc_ct_handle_unsol_abort(struct lpfc_hba *, struct hbq_dmabuf *);
int lpfc_ns_cmd(struct lpfc_vport *, int, uint8_t, uint32_t);
int lpfc_fdmi_cmd(struct lpfc_vport *, struct lpfc_nodelist *, int);
void lpfc_fdmi_tmo(unsigned long);
@@ -427,6 +426,7 @@ int lpfc_bsg_request(struct fc_bsg_job *);
int lpfc_bsg_timeout(struct fc_bsg_job *);
int lpfc_bsg_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
			     struct lpfc_iocbq *);
int lpfc_bsg_ct_unsol_abort(struct lpfc_hba *, struct hbq_dmabuf *);
void __lpfc_sli_ringtx_put(struct lpfc_hba *, struct lpfc_sli_ring *,
	struct lpfc_iocbq *);
struct lpfc_iocbq *lpfc_sli_ringtx_get(struct lpfc_hba *,
+12 −25
Original line number Diff line number Diff line
@@ -164,37 +164,24 @@ lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}

/**
 * lpfc_sli4_ct_abort_unsol_event - Default handle for sli4 unsol abort
 * lpfc_ct_handle_unsol_abort - ct upper level protocol abort handler
 * @phba: Pointer to HBA context object.
 * @pring: Pointer to the driver internal I/O ring.
 * @piocbq: Pointer to the IOCBQ.
 * @dmabuf: pointer to a dmabuf that describes the FC sequence
 *
 * This function serves as the default handler for the sli4 unsolicited
 * abort event. It shall be invoked when there is no application interface
 * registered unsolicited abort handler. This handler does nothing but
 * just simply releases the dma buffer used by the unsol abort event.
 * This function serves as the upper level protocol abort handler for CT
 * protocol.
 *
 * Return 1 if abort has been handled, 0 otherwise.
 **/
void
lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *phba,
			       struct lpfc_sli_ring *pring,
			       struct lpfc_iocbq *piocbq)
int
lpfc_ct_handle_unsol_abort(struct lpfc_hba *phba, struct hbq_dmabuf *dmabuf)
{
	IOCB_t *icmd = &piocbq->iocb;
	struct lpfc_dmabuf *bdeBuf;
	uint32_t size;
	int handled;

	/* Forward abort event to any process registered to receive ct event */
	if (lpfc_bsg_ct_unsol_event(phba, pring, piocbq) == 0)
		return;
	/* CT upper level goes through BSG */
	handled = lpfc_bsg_ct_unsol_abort(phba, dmabuf);

	/* If there is no BDE associated with IOCB, there is nothing to do */
	if (icmd->ulpBdeCount == 0)
		return;
	bdeBuf = piocbq->context2;
	piocbq->context2 = NULL;
	size  = icmd->un.cont64[0].tus.f.bdeSize;
	lpfc_ct_unsol_buffer(phba, piocbq, bdeBuf, size);
	lpfc_in_buf_free(phba, bdeBuf);
	return handled;
}

static void
+103 −48
Original line number Diff line number Diff line
@@ -8855,12 +8855,6 @@ lpfc_sli_setup(struct lpfc_hba *phba)
			pring->prt[3].type = FC_TYPE_CT;
			pring->prt[3].lpfc_sli_rcv_unsol_event =
			    lpfc_ct_unsol_event;
			/* abort unsolicited sequence */
			pring->prt[4].profile = 0;	/* Mask 4 */
			pring->prt[4].rctl = FC_RCTL_BA_ABTS;
			pring->prt[4].type = FC_TYPE_BLS;
			pring->prt[4].lpfc_sli_rcv_unsol_event =
			    lpfc_sli4_ct_abort_unsol_event;
			break;
		}
		totiocbsize += (pring->sli.sli3.numCiocb *
@@ -14062,6 +14056,40 @@ lpfc_sli4_abort_partial_seq(struct lpfc_vport *vport,
	return false;
}

/**
 * lpfc_sli4_abort_ulp_seq - Abort assembled unsol sequence from ulp
 * @vport: pointer to a vitural port
 * @dmabuf: pointer to a dmabuf that describes the FC sequence
 *
 * This function tries to abort from the assembed sequence from upper level
 * protocol, described by the information from basic abbort @dmabuf. It
 * checks to see whether such pending context exists at upper level protocol.
 * If so, it shall clean up the pending context.
 *
 * Return
 * true  -- if there is matching pending context of the sequence cleaned
 *          at ulp;
 * false -- if there is no matching pending context of the sequence present
 *          at ulp.
 **/
static bool
lpfc_sli4_abort_ulp_seq(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
{
	struct lpfc_hba *phba = vport->phba;
	int handled;

	/* Accepting abort at ulp with SLI4 only */
	if (phba->sli_rev < LPFC_SLI_REV4)
		return false;

	/* Register all caring upper level protocols to attend abort */
	handled = lpfc_ct_handle_unsol_abort(phba, dmabuf);
	if (handled)
		return true;

	return false;
}

/**
 * lpfc_sli4_seq_abort_rsp_cmpl - BLS ABORT RSP seq abort iocb complete handler
 * @phba: Pointer to HBA context object.
@@ -14077,8 +14105,14 @@ lpfc_sli4_seq_abort_rsp_cmpl(struct lpfc_hba *phba,
			     struct lpfc_iocbq *cmd_iocbq,
			     struct lpfc_iocbq *rsp_iocbq)
{
	if (cmd_iocbq)
	struct lpfc_nodelist *ndlp;

	if (cmd_iocbq) {
		ndlp = (struct lpfc_nodelist *)cmd_iocbq->context1;
		lpfc_nlp_put(ndlp);
		lpfc_nlp_not_used(ndlp);
		lpfc_sli_release_iocbq(phba, cmd_iocbq);
	}

	/* Failure means BLS ABORT RSP did not get delivered to remote node*/
	if (rsp_iocbq && rsp_iocbq->iocb.ulpStatus)
@@ -14118,9 +14152,10 @@ lpfc_sli4_xri_inrange(struct lpfc_hba *phba,
 * event after aborting the sequence handling.
 **/
static void
lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba,
			struct fc_frame_header *fc_hdr)
lpfc_sli4_seq_abort_rsp(struct lpfc_vport *vport,
			struct fc_frame_header *fc_hdr, bool aborted)
{
	struct lpfc_hba *phba = vport->phba;
	struct lpfc_iocbq *ctiocb = NULL;
	struct lpfc_nodelist *ndlp;
	uint16_t oxid, rxid, xri, lxri;
@@ -14135,13 +14170,28 @@ lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba,
	oxid = be16_to_cpu(fc_hdr->fh_ox_id);
	rxid = be16_to_cpu(fc_hdr->fh_rx_id);

	ndlp = lpfc_findnode_did(phba->pport, sid);
	ndlp = lpfc_findnode_did(vport, sid);
	if (!ndlp) {
		lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
				"1268 Find ndlp returned NULL for oxid:x%x "
				"SID:x%x\n", oxid, sid);
		ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
		if (!ndlp) {
			lpfc_printf_vlog(vport, KERN_WARNING, LOG_ELS,
					 "1268 Failed to allocate ndlp for "
					 "oxid:x%x SID:x%x\n", oxid, sid);
			return;
		}
		lpfc_nlp_init(vport, ndlp, sid);
		/* Put ndlp onto pport node list */
		lpfc_enqueue_node(vport, ndlp);
	} else if (!NLP_CHK_NODE_ACT(ndlp)) {
		/* re-setup ndlp without removing from node list */
		ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE);
		if (!ndlp) {
			lpfc_printf_vlog(vport, KERN_WARNING, LOG_ELS,
					 "3275 Failed to active ndlp found "
					 "for oxid:x%x SID:x%x\n", oxid, sid);
			return;
		}
	}

	/* Allocate buffer for rsp iocb */
	ctiocb = lpfc_sli_get_iocbq(phba);
@@ -14164,7 +14214,7 @@ lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba,
	icmd->ulpLe = 1;
	icmd->ulpClass = CLASS3;
	icmd->ulpContext = phba->sli4_hba.rpi_ids[ndlp->nlp_rpi];
	ctiocb->context1 = ndlp;
	ctiocb->context1 = lpfc_nlp_get(ndlp);

	ctiocb->iocb_cmpl = NULL;
	ctiocb->vport = phba->pport;
@@ -14183,14 +14233,24 @@ lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba,
	if (lxri != NO_XRI)
		lpfc_set_rrq_active(phba, ndlp, lxri,
			(xri == oxid) ? rxid : oxid, 0);
	/* If the oxid maps to the FCP XRI range or if it is out of range,
	 * send a BLS_RJT.  The driver no longer has that exchange.
	 * Override the IOCB for a BA_RJT.
	 */
	if (xri > (phba->sli4_hba.max_cfg_param.max_xri +
		    phba->sli4_hba.max_cfg_param.xri_base) ||
	    xri > (lpfc_sli4_get_els_iocb_cnt(phba) +
		    phba->sli4_hba.max_cfg_param.xri_base)) {
	/* For BA_ABTS from exchange responder, if the logical xri with
	 * the oxid maps to the FCP XRI range, the port no longer has
	 * that exchange context, send a BLS_RJT. Override the IOCB for
	 * a BA_RJT.
	 */
	if ((fctl & FC_FC_EX_CTX) &&
	    (lxri > lpfc_sli4_get_els_iocb_cnt(phba))) {
		icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_BA_RJT;
		bf_set(lpfc_vndr_code, &icmd->un.bls_rsp, 0);
		bf_set(lpfc_rsn_expln, &icmd->un.bls_rsp, FC_BA_RJT_INV_XID);
		bf_set(lpfc_rsn_code, &icmd->un.bls_rsp, FC_BA_RJT_UNABLE);
	}

	/* If BA_ABTS failed to abort a partially assembled receive sequence,
	 * the driver no longer has that exchange, send a BLS_RJT. Override
	 * the IOCB for a BA_RJT.
	 */
	if (aborted == false) {
		icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_BA_RJT;
		bf_set(lpfc_vndr_code, &icmd->un.bls_rsp, 0);
		bf_set(lpfc_rsn_expln, &icmd->un.bls_rsp, FC_BA_RJT_INV_XID);
@@ -14214,17 +14274,19 @@ lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba,
	bf_set(lpfc_abts_oxid, &icmd->un.bls_rsp, oxid);

	/* Xmit CT abts response on exchange <xid> */
	lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
			 "1200 Send BLS cmd x%x on oxid x%x Data: x%x\n",
			 icmd->un.xseq64.w5.hcsw.Rctl, oxid, phba->link_state);

	rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0);
	if (rc == IOCB_ERROR) {
		lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
		lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
				 "2925 Failed to issue CT ABTS RSP x%x on "
				 "xri x%x, Data x%x\n",
				 icmd->un.xseq64.w5.hcsw.Rctl, oxid,
				 phba->link_state);
		lpfc_nlp_put(ndlp);
		ctiocb->context1 = NULL;
		lpfc_sli_release_iocbq(phba, ctiocb);
	}
}
@@ -14249,32 +14311,25 @@ lpfc_sli4_handle_unsol_abort(struct lpfc_vport *vport,
	struct lpfc_hba *phba = vport->phba;
	struct fc_frame_header fc_hdr;
	uint32_t fctl;
	bool abts_par;
	bool aborted;

	/* Make a copy of fc_hdr before the dmabuf being released */
	memcpy(&fc_hdr, dmabuf->hbuf.virt, sizeof(struct fc_frame_header));
	fctl = sli4_fctl_from_fc_hdr(&fc_hdr);

	if (fctl & FC_FC_EX_CTX) {
		/*
		 * ABTS sent by responder to exchange, just free the buffer
		 */
		lpfc_in_buf_free(phba, &dmabuf->dbuf);
		/* ABTS by responder to exchange, no cleanup needed */
		aborted = true;
	} else {
		/*
		 * ABTS sent by initiator to exchange, need to do cleanup
		 */
		/* Try to abort partially assembled seq */
		abts_par = lpfc_sli4_abort_partial_seq(vport, dmabuf);

		/* Send abort to ULP if partially seq abort failed */
		if (abts_par == false)
			lpfc_sli4_send_seq_to_ulp(vport, dmabuf);
		else
			lpfc_in_buf_free(phba, &dmabuf->dbuf);
		/* ABTS by initiator to exchange, need to do cleanup */
		aborted = lpfc_sli4_abort_partial_seq(vport, dmabuf);
		if (aborted == false)
			aborted = lpfc_sli4_abort_ulp_seq(vport, dmabuf);
	}
	/* Send basic accept (BA_ACC) to the abort requester */
	lpfc_sli4_seq_abort_rsp(phba, &fc_hdr);
	lpfc_in_buf_free(phba, &dmabuf->dbuf);

	/* Respond with BA_ACC or BA_RJT accordingly */
	lpfc_sli4_seq_abort_rsp(vport, &fc_hdr, aborted);
}

/**