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

[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
......@@ -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 */
......
......@@ -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 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
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;
......
......@@ -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 *,
......
......@@ -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
......
......@@ -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,12 +14170,27 @@ 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);
return;
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 */
......@@ -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.
/* 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 (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)) {
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,
"1200 Send BLS cmd x%x on oxid x%x Data: x%x\n",
icmd->un.xseq64.w5.hcsw.Rctl, oxid, phba->link_state);
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,
"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_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);
}
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment