/******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2025 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * * Public License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful. * * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * * TO BE LEGALLY INVALID. See the GNU General Public License for * * more details, a copy of which can be found in the file COPYING * * included with this package. *
*******************************************************************/
/* * Fibre Channel SCSI LAN Device Driver CT support: FC Generic Services FC-GS
*/
/** * lpfc_ct_unsol_cmpl : Completion callback function for unsol ct commands * @phba : pointer to lpfc hba data structure. * @cmdiocb : pointer to lpfc command iocb data structure. * @rspiocb : pointer to lpfc response iocb data structure. * * This routine is the callback function for issuing unsol ct reject command. * The memory allocated in the reject command path is freed up here.
**/ staticvoid
lpfc_ct_unsol_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, struct lpfc_iocbq *rspiocb)
{ struct lpfc_nodelist *ndlp; struct lpfc_dmabuf *mp, *bmp;
ndlp = cmdiocb->ndlp; if (ndlp)
lpfc_nlp_put(ndlp);
/* Save for completion so we can release these resources */
cmdiocbq->rsp_dmabuf = mp;
cmdiocbq->bpl_dmabuf = bmp;
cmdiocbq->cmd_cmpl = lpfc_ct_unsol_cmpl;
tmo = (3 * phba->fc_ratov);
/** * lpfc_ct_handle_mibreq - Process an unsolicited CT MIB request data buffer * @phba: pointer to lpfc hba data structure. * @ctiocbq: pointer to lpfc CT command iocb data structure. * * This routine is used for processing the IOCB associated with a unsolicited * CT MIB request. It first determines whether there is an existing ndlp that * matches the DID from the unsolicited IOCB. If not, it will return.
**/ staticvoid
lpfc_ct_handle_mibreq(struct lpfc_hba *phba, struct lpfc_iocbq *ctiocbq)
{ struct lpfc_sli_ct_request *ct_req; struct lpfc_nodelist *ndlp = NULL; struct lpfc_vport *vport = ctiocbq->vport;
u32 ulp_status = get_job_ulpstatus(phba, ctiocbq);
u32 ulp_word4 = get_job_word4(phba, ctiocbq);
u32 did;
u16 mi_cmd;
did = bf_get(els_rsp64_sid, &ctiocbq->wqe.xmit_els_rsp); if (ulp_status) {
lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS, "6438 Unsol CT: status:x%x/x%x did : x%x\n",
ulp_status, ulp_word4, did); return;
}
/* Ignore traffic received during vport shutdown */ if (test_bit(FC_UNLOADING, &vport->load_flag)) return;
ndlp = lpfc_findnode_did(vport, did); if (!ndlp) {
lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS, "6439 Unsol CT: NDLP Not Found for DID : x%x",
did); return;
}
mi_cmd = be16_to_cpu(ct_req->CommandResponse.bits.CmdRsp);
lpfc_vlog_msg(vport, KERN_WARNING, LOG_ELS, "6442 MI Cmd: x%x Not Supported\n", mi_cmd);
lpfc_ct_reject_event(ndlp, ct_req,
bf_get(wqe_ctxt_tag,
&ctiocbq->wqe.xmit_els_rsp.wqe_com),
bf_get(wqe_rcvoxid,
&ctiocbq->wqe.xmit_els_rsp.wqe_com));
}
/** * lpfc_ct_unsol_event - Process an unsolicited event from a ct sli ring * @phba: pointer to lpfc hba data structure. * @pring: pointer to a SLI ring. * @ctiocbq: pointer to lpfc ct iocb data structure. * * This routine is used to process an unsolicited event received from a SLI * (Service Level Interface) ring. The actual processing of the data buffer * associated with the unsolicited event is done by invoking appropriate routine * after properly set up the iocb buffer from the SLI ring on which the * unsolicited event was received.
**/ void
lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, struct lpfc_iocbq *ctiocbq)
{ struct lpfc_dmabuf *mp = NULL;
IOCB_t *icmd = &ctiocbq->iocb; int i; struct lpfc_iocbq *iocbq; struct lpfc_iocbq *iocb;
dma_addr_t dma_addr;
uint32_t size; struct list_head head; struct lpfc_sli_ct_request *ct_req; struct lpfc_dmabuf *bdeBuf1 = ctiocbq->cmd_dmabuf; struct lpfc_dmabuf *bdeBuf2 = ctiocbq->bpl_dmabuf;
u32 status, parameter, bde_count = 0; struct lpfc_wcqe_complete *wcqe_cmpl = NULL;
/** * lpfc_ct_handle_unsol_abort - ct upper level protocol abort handler * @phba: Pointer to HBA context object. * @dmabuf: pointer to a dmabuf that describes the FC sequence * * This function serves as the upper level protocol abort handler for CT * protocol. * * Return 1 if abort has been handled, 0 otherwise.
**/ int
lpfc_ct_handle_unsol_abort(struct lpfc_hba *phba, struct hbq_dmabuf *dmabuf)
{ int handled;
/* * lpfc_gen_req - Build and issue a GEN_REQUEST command to the SLI Layer * @vport: pointer to a host virtual N_Port data structure. * @bmp: Pointer to BPL for SLI command * @inp: Pointer to data buffer for response data. * @outp: Pointer to data buffer that hold the CT command. * @cmpl: completion routine to call when command completes * @ndlp: Destination NPort nodelist entry * * This function as the final part for issuing a CT command.
*/ staticint
lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp, struct lpfc_dmabuf *inp, struct lpfc_dmabuf *outp, void (*cmpl)(struct lpfc_hba *, struct lpfc_iocbq *, struct lpfc_iocbq *), struct lpfc_nodelist *ndlp, uint32_t event_tag, uint32_t num_entry,
uint32_t tmo, uint8_t retry)
{ struct lpfc_hba *phba = vport->phba; struct lpfc_iocbq *geniocb; int rc;
u16 ulp_context;
/* Allocate buffer for command iocb */
geniocb = lpfc_sli_get_iocbq(phba);
if (geniocb == NULL) return 1;
/* Update the num_entry bde count */
geniocb->num_bdes = num_entry;
geniocb->bpl_dmabuf = bmp;
/* Save for completion so we can release these resources */
geniocb->cmd_dmabuf = inp;
geniocb->rsp_dmabuf = outp;
geniocb->event_tag = event_tag;
if (!tmo) { /* FC spec states we need 3 * ratov for CT requests */
tmo = (3 * phba->fc_ratov);
}
/* * lpfc_ct_cmd - Build and issue a CT command * @vport: pointer to a host virtual N_Port data structure. * @inmp: Pointer to data buffer for response data. * @bmp: Pointer to BPL for SLI command * @ndlp: Destination NPort nodelist entry * @cmpl: completion routine to call when command completes * * This function is called for issuing a CT command.
*/ staticint
lpfc_ct_cmd(struct lpfc_vport *vport, struct lpfc_dmabuf *inmp, struct lpfc_dmabuf *bmp, struct lpfc_nodelist *ndlp, void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, struct lpfc_iocbq *),
uint32_t rsp_size, uint8_t retry)
{ struct lpfc_hba *phba = vport->phba; struct ulp_bde64 *bpl = (struct ulp_bde64 *) bmp->virt; struct lpfc_dmabuf *outmp; int cnt = 0, status;
__be16 cmdcode = ((struct lpfc_sli_ct_request *)inmp->virt)->
CommandResponse.bits.CmdRsp;
bpl++; /* Skip past ct request */
/* Put buffer(s) for ct rsp in bpl */
outmp = lpfc_alloc_ct_rsp(phba, cmdcode, bpl, rsp_size, &cnt); if (!outmp) return -ENOMEM; /* * Form the CT IOCB. The total number of BDEs in this IOCB * is the single command plus response count from * lpfc_alloc_ct_rsp.
*/
cnt += 1;
status = lpfc_gen_req(vport, bmp, inmp, outmp, cmpl, ndlp,
phba->fc_eventTag, cnt, 0, retry); if (status) {
lpfc_free_ct_rsp(phba, outmp); return -ENOMEM;
} return 0;
}
/* if ndlp needs to be discovered and prior * state of ndlp hit devloss, change state to * allow rediscovery.
*/ if (test_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag) &&
ndlp->nlp_state == NLP_STE_UNUSED_NODE) {
lpfc_nlp_set_state(vport, ndlp,
NLP_STE_NPR_NODE);
}
} else {
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT, "Skip1 GID_FTrsp: did:x%x flg:x%lx cnt:%d",
Did, vport->fc_flag, vport->fc_rscn_id_cnt);
if (!ctptr) {
ctptr = (uint32_t *) mlast->virt;
} else
Cnt -= 16; /* subtract length of CT header */
/* Loop through entire NameServer list of DIDs */ while (Cnt >= sizeof(uint32_t)) { /* Get next DID from NameServer List */
CTentry = *ctptr++;
Did = ((be32_to_cpu(CTentry)) & Mask_DID);
lpfc_ns_rsp_audit_did(vport, Did, fc4_type); if (CTentry & (cpu_to_be32(SLI_CT_LAST_ENTRY))) goto nsout1;
Cnt -= sizeof(uint32_t);
}
ctptr = NULL;
}
/* All GID_FT entries processed. If the driver is running in * in target mode, put impacted nodes into recovery and drop * the RPI to flush outstanding IO.
*/ if (vport->phba->nvmet_support) {
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { if (!test_bit(NLP_NVMET_RECOV, &ndlp->nlp_flag)) continue;
lpfc_disc_state_machine(vport, ndlp, NULL,
NLP_EVT_DEVICE_RECOVERY);
clear_bit(NLP_NVMET_RECOV, &ndlp->nlp_flag);
}
}
/* Ignore response if link flipped after this request was made */ if (cmdiocb->event_tag != phba->fc_eventTag) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "9043 Event tag mismatch. Ignoring NS rsp\n"); goto out;
}
/* Skip processing response on pport if unloading */ if (vport == phba->pport && test_bit(FC_UNLOADING, &vport->load_flag)) { if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
lpfc_els_flush_rscn(vport); goto out;
}
if (lpfc_els_chk_latt(vport)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "0216 Link event during NS query\n"); if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
lpfc_els_flush_rscn(vport);
lpfc_vport_set_state(vport, FC_VPORT_FAILED); goto out;
} if (lpfc_error_lost_link(vport, ulp_status, ulp_word4)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "0226 NS query failed due to link event: " "ulp_status x%x ulp_word4 x%x fc_flag x%lx " "port_state x%x gidft_inp x%x\n",
ulp_status, ulp_word4, vport->fc_flag,
vport->port_state, vport->gidft_inp); if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
lpfc_els_flush_rscn(vport); if (vport->gidft_inp)
vport->gidft_inp--; goto out;
}
if (test_and_clear_bit(FC_RSCN_DEFERRED, &vport->fc_flag)) { /* This is a GID_FT completing so the gidft_inp counter was * incremented before the GID_FT was issued to the wire.
*/ if (vport->gidft_inp)
vport->gidft_inp--;
/* * Skip processing the NS response * Re-issue the NS cmd
*/
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0151 Process Deferred RSCN Data: x%lx x%x\n",
vport->fc_flag, vport->fc_rscn_id_cnt);
lpfc_els_handle_rscn(vport);
goto out;
}
if (ulp_status) { /* Check for retry */ if (vport->fc_ns_retry < LPFC_MAX_NS_RETRY) { if (ulp_status != IOSTAT_LOCAL_REJECT ||
(ulp_word4 & IOERR_PARAM_MASK) !=
IOERR_NO_RESOURCES)
vport->fc_ns_retry++;
type = lpfc_get_gidft_type(vport, cmdiocb); if (type == 0) goto out;
/* Link up / RSCN discovery */ if ((vport->num_disc_nodes == 0) &&
(vport->gidft_inp == 0)) { /* * The driver has cycled through all Nports in the RSCN payload. * Complete the handling by cleaning up and marking the * current driver state.
*/ if (vport->port_state >= LPFC_DISC_AUTH) { if (test_bit(FC_RSCN_MODE, &vport->fc_flag)) {
lpfc_els_flush_rscn(vport); /* RSCN still */
set_bit(FC_RSCN_MODE, &vport->fc_flag);
} else {
lpfc_els_flush_rscn(vport);
}
}
/* Ignore response if link flipped after this request was made */ if (cmdiocb->event_tag != phba->fc_eventTag) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "9044 Event tag mismatch. Ignoring NS rsp\n"); goto out;
}
/* Skip processing response on pport if unloading */ if (vport == phba->pport && test_bit(FC_UNLOADING, &vport->load_flag)) { if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
lpfc_els_flush_rscn(vport); goto out;
}
if (lpfc_els_chk_latt(vport)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "4108 Link event during NS query\n"); if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
lpfc_els_flush_rscn(vport);
lpfc_vport_set_state(vport, FC_VPORT_FAILED); goto out;
} if (lpfc_error_lost_link(vport, ulp_status, ulp_word4)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "4166 NS query failed due to link event: " "ulp_status x%x ulp_word4 x%x fc_flag x%lx " "port_state x%x gidft_inp x%x\n",
ulp_status, ulp_word4, vport->fc_flag,
vport->port_state, vport->gidft_inp); if (test_bit(FC_RSCN_MODE, &vport->fc_flag))
lpfc_els_flush_rscn(vport); if (vport->gidft_inp)
vport->gidft_inp--; goto out;
}
if (test_and_clear_bit(FC_RSCN_DEFERRED, &vport->fc_flag)) { /* This is a GID_PT completing so the gidft_inp counter was * incremented before the GID_PT was issued to the wire.
*/ if (vport->gidft_inp)
vport->gidft_inp--;
/* * Skip processing the NS response * Re-issue the NS cmd
*/
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "4167 Process Deferred RSCN Data: x%lx x%x\n",
vport->fc_flag, vport->fc_rscn_id_cnt);
lpfc_els_handle_rscn(vport);
goto out;
}
if (ulp_status) { /* Check for retry */ if (vport->fc_ns_retry < LPFC_MAX_NS_RETRY) { if (ulp_status != IOSTAT_LOCAL_REJECT ||
(ulp_word4 & IOERR_PARAM_MASK) !=
IOERR_NO_RESOURCES)
vport->fc_ns_retry++;
/* Link up / RSCN discovery */ if ((vport->num_disc_nodes == 0) &&
(vport->gidft_inp == 0)) { /* * The driver has cycled through all Nports in the RSCN payload. * Complete the handling by cleaning up and marking the * current driver state.
*/ if (vport->port_state >= LPFC_DISC_AUTH) { if (test_bit(FC_RSCN_MODE, &vport->fc_flag)) {
lpfc_els_flush_rscn(vport); /* RSCN still */
set_bit(FC_RSCN_MODE, &vport->fc_flag);
} else {
lpfc_els_flush_rscn(vport);
}
}
/* Ignore response if link flipped after this request was made */ if (cmdiocb->event_tag != phba->fc_eventTag) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "9045 Event tag mismatch. Ignoring NS rsp\n"); goto iocb_free;
}
if (ulp_status == IOSTAT_SUCCESS) { /* Good status, continue checking */
CTrsp = (struct lpfc_sli_ct_request *) outp->virt;
fbits = CTrsp->un.gff_acc.fbits[FCP_TYPE_FEATURE_OFFSET];
case IOERR_NO_RESOURCES: /* We don't increment the retry * count for this case.
*/ break; case IOERR_LINK_DOWN: case IOERR_SLI_ABORTED: case IOERR_SLI_DOWN:
retry = 0; break; default:
cmdiocb->retry++;
}
} else
cmdiocb->retry++;
if (vport->num_disc_nodes == 0) { /* * The driver has cycled through all Nports in the RSCN payload. * Complete the handling by cleaning up and marking the * current driver state.
*/ if (vport->port_state >= LPFC_DISC_AUTH) { if (test_bit(FC_RSCN_MODE, &vport->fc_flag)) {
lpfc_els_flush_rscn(vport); /* RSCN still */
set_bit(FC_RSCN_MODE, &vport->fc_flag);
} else {
lpfc_els_flush_rscn(vport);
}
}
lpfc_disc_start(vport);
}
/* Ignore response if link flipped after this request was made */ if ((uint32_t)cmdiocb->event_tag != phba->fc_eventTag) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "9046 Event tag mismatch. Ignoring NS rsp\n"); goto out;
}
/* Lookup the NPort_ID queried in the GFT_ID and find the * driver's local node. It's an error if the driver * doesn't have one.
*/
ndlp = lpfc_findnode_did(vport, did); if (ndlp) { /* The bitmask value for FCP and NVME FCP types is * the same because they are 32 bits distant from * each other in word0 and word0.
*/ if (fc4_data_0 & LPFC_FC4_TYPE_BITMASK)
ndlp->nlp_fc4_type |= NLP_FC4_FCP; if (fc4_data_1 & LPFC_FC4_TYPE_BITMASK)
ndlp->nlp_fc4_type |= NLP_FC4_NVME;
lpfc_printf_vlog(vport, KERN_INFO,
LOG_DISCOVERY | LOG_NODE, "3064 Setting ndlp x%px, DID x%06x " "with FC4 x%08x, Data: x%08x x%08x " "%d\n",
ndlp, did, ndlp->nlp_fc4_type,
FC_TYPE_FCP, FC_TYPE_NVME,
ndlp->nlp_state);
if (ndlp->nlp_state == NLP_STE_REG_LOGIN_ISSUE &&
ndlp->nlp_fc4_type) {
ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE;
lpfc_nlp_set_state(vport, ndlp,
NLP_STE_PRLI_ISSUE);
lpfc_issue_els_prli(vport, ndlp, 0);
} elseif (!ndlp->nlp_fc4_type) { /* If fc4 type is still unknown, then LOGO */
lpfc_printf_vlog(vport, KERN_INFO,
LOG_DISCOVERY | LOG_NODE, "6443 Sending LOGO ndlp x%px, " "DID x%06x with fc4_type: " "x%08x, state: %d\n",
ndlp, did, ndlp->nlp_fc4_type,
ndlp->nlp_state);
lpfc_issue_els_logo(vport, ndlp, 0);
ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE;
lpfc_nlp_set_state(vport, ndlp,
NLP_STE_NPR_NODE);
}
}
} else
lpfc_vlog_msg(vport, KERN_WARNING, LOG_DISCOVERY, "3065 GFT_ID status x%08x\n", ulp_status);
out: /* If the caller wanted a synchronous DA_ID completion, signal the * wait obj and clear flag to reset the vport.
*/ if (test_bit(NLP_WAIT_FOR_DA_ID, &ndlp->save_flags)) { if (ndlp->da_id_waitq)
wake_up(ndlp->da_id_waitq);
}
/* * Although the symbolic port name is thought to be an integer * as of January 18, 2016, leave it as a string until more of * the record state becomes defined.
*/ int
lpfc_vport_symbolic_port_name(struct lpfc_vport *vport, char *symbol,
size_t size)
{ int n;
/* * Use the lpfc board number as the Symbolic Port * Name object. NPIV is not in play so this integer * value is sufficient and unique per FC-ID.
*/
n = scnprintf(symbol, size, "%d", vport->phba->brd_no); return n;
}
/* * This routine will return the FC4 Type associated with the CT * GID_FT command.
*/ int
lpfc_get_gidft_type(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb)
{ struct lpfc_sli_ct_request *CtReq; struct lpfc_dmabuf *mp;
uint32_t type;
mp = cmdiocb->cmd_dmabuf; if (mp == NULL) return 0;
CtReq = (struct lpfc_sli_ct_request *)mp->virt;
type = (uint32_t)CtReq->un.gid.Fc4Type; if ((type != SLI_CTPT_FCP) && (type != SLI_CTPT_NVME)) return 0; return type;
}
/* The driver always supports FC_TYPE_FCP. However, the * caller can specify NVME (type x28) as well. But only * these that FC4 type is supported.
*/ if (((vport->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) ||
(vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) &&
(context == FC_TYPE_NVME)) { if ((vport == phba->pport) && phba->nvmet_support) {
CtReq->un.rff.fbits = (FC4_FEATURE_TARGET |
FC4_FEATURE_NVME_DISC);
lpfc_nvmet_update_targetport(phba);
} else {
lpfc_nvme_update_localport(vport);
}
CtReq->un.rff.type_code = context;
/** * lpfc_fdmi_rprt_defer - Check for any deferred FDMI RPRT commands * @phba: Pointer to HBA context object. * @mask: Initial port attributes mask * * This function checks to see if any vports have deferred their FDMI RPRT. * A vports RPRT may be deferred if it is issued before the primary ports * RHBA completes.
*/ staticvoid
lpfc_fdmi_rprt_defer(struct lpfc_hba *phba, uint32_t mask)
{ struct lpfc_vport **vports; struct lpfc_vport *vport; struct lpfc_nodelist *ndlp; int i;
set_bit(HBA_RHBA_CMPL, &phba->hba_flag);
vports = lpfc_create_vport_work_array(phba); if (vports) { for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
vport = vports[i];
ndlp = lpfc_findnode_did(phba->pport, FDMI_DID); if (!ndlp) continue; if (vport->ct_flags & FC_CT_RPRT_DEFER) {
vport->ct_flags &= ~FC_CT_RPRT_DEFER;
vport->fdmi_port_mask = mask;
lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPRT, 0);
}
}
}
lpfc_destroy_vport_work_array(phba, vports);
}
/** * lpfc_cmpl_ct_disc_fdmi - Handle a discovery FDMI completion * @phba: Pointer to HBA context object. * @cmdiocb: Pointer to the command IOCBQ. * @rspiocb: Pointer to the response IOCBQ. * * This function to handle the completion of a driver initiated FDMI * CT command issued during discovery.
*/ staticvoid
lpfc_cmpl_ct_disc_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, struct lpfc_iocbq *rspiocb)
{ struct lpfc_vport *vport = cmdiocb->vport; struct lpfc_dmabuf *inp = cmdiocb->cmd_dmabuf; struct lpfc_dmabuf *outp = cmdiocb->rsp_dmabuf; struct lpfc_sli_ct_request *CTcmd = inp->virt; struct lpfc_sli_ct_request *CTrsp = outp->virt;
__be16 fdmi_cmd = CTcmd->CommandResponse.bits.CmdRsp;
__be16 fdmi_rsp = CTrsp->CommandResponse.bits.CmdRsp; struct lpfc_nodelist *ndlp, *free_ndlp = NULL;
uint32_t latt, cmd, err;
u32 ulp_status = get_job_ulpstatus(phba, rspiocb);
u32 ulp_word4 = get_job_word4(phba, rspiocb);
/* Look for a retryable error */ if (ulp_status == IOSTAT_LOCAL_REJECT) { switch ((ulp_word4 & IOERR_PARAM_MASK)) { case IOERR_ABORT_IN_PROGRESS: case IOERR_SEQUENCE_TIMEOUT: case IOERR_ILLEGAL_FRAME: case IOERR_NO_RESOURCES: case IOERR_ILLEGAL_COMMAND:
cmdiocb->retry++; if (cmdiocb->retry >= LPFC_FDMI_MAX_RETRY) break;
/* Retry the same FDMI command */
err = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING,
cmdiocb, 0); if (err == IOCB_ERROR) break; return; default: break;
}
}
}
/* Should we fallback to FDMI-2 / FDMI-1 ? */ switch (cmd) { case SLI_MGMT_RHBA: if (vport->fdmi_hba_mask == LPFC_FDMI2_HBA_ATTR) { /* Fallback to FDMI-1 for HBA attributes */
vport->fdmi_hba_mask = LPFC_FDMI1_HBA_ATTR;
/* If HBA attributes are FDMI1, so should * port attributes be for consistency.
*/
vport->fdmi_port_mask = LPFC_FDMI1_PORT_ATTR; /* Start over */
lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA, 0);
} return;
case SLI_MGMT_RPRT: if (vport->port_type != LPFC_PHYSICAL_PORT) {
ndlp = lpfc_findnode_did(phba->pport, FDMI_DID); if (!ndlp) return;
} if (vport->fdmi_port_mask == LPFC_FDMI2_PORT_ATTR) { /* Fallback to FDMI-1 */
vport->fdmi_port_mask = LPFC_FDMI1_PORT_ATTR; /* Start over */
lpfc_fdmi_cmd(vport, ndlp, cmd, 0); return;
} if (vport->fdmi_port_mask == LPFC_FDMI2_SMART_ATTR) {
vport->fdmi_port_mask = LPFC_FDMI2_PORT_ATTR; /* Retry the same command */
lpfc_fdmi_cmd(vport, ndlp, cmd, 0);
} return;
case SLI_MGMT_RPA: /* No retry on Vendor, RPA only done on physical port */ if (phba->link_flag & LS_CT_VEN_RPA) {
phba->link_flag &= ~LS_CT_VEN_RPA; if (phba->cmf_active_mode == LPFC_CFG_OFF) return;
lpfc_printf_log(phba, KERN_WARNING,
LOG_DISCOVERY | LOG_ELS, "6460 VEN FDMI RPA RJT\n"); return;
} if (vport->fdmi_port_mask == LPFC_FDMI2_PORT_ATTR) { /* Fallback to FDMI-1 */
vport->fdmi_hba_mask = LPFC_FDMI1_HBA_ATTR;
vport->fdmi_port_mask = LPFC_FDMI1_PORT_ATTR; /* Start over */
lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA, 0); return;
} if (vport->fdmi_port_mask == LPFC_FDMI2_SMART_ATTR) {
vport->fdmi_port_mask = LPFC_FDMI2_PORT_ATTR; /* Retry the same command */
lpfc_fdmi_cmd(vport, ndlp, cmd, 0);
} return;
}
}
/* * On success, need to cycle thru FDMI registration for discovery * DHBA -> DPRT -> RHBA -> RPA (physical port) * DPRT -> RPRT (vports)
*/ switch (cmd) { case SLI_MGMT_RHBA: /* Check for any RPRTs deferred till after RHBA completes */
lpfc_fdmi_rprt_defer(phba, vport->fdmi_port_mask);
case SLI_MGMT_DHBA:
lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DPRT, 0); break;
case SLI_MGMT_DPRT: if (vport->port_type == LPFC_PHYSICAL_PORT) {
lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RHBA, 0);
} else {
ndlp = lpfc_findnode_did(phba->pport, FDMI_DID); if (!ndlp) return;
/* Only issue a RPRT for the vport if the RHBA * for the physical port completes successfully. * We may have to defer the RPRT accordingly.
*/ if (test_bit(HBA_RHBA_CMPL, &phba->hba_flag)) {
lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPRT, 0);
} else {
lpfc_printf_vlog(vport, KERN_INFO,
LOG_DISCOVERY, "6078 RPRT deferred\n");
vport->ct_flags |= FC_CT_RPRT_DEFER;
}
} break; case SLI_MGMT_RPA: if (vport->port_type == LPFC_PHYSICAL_PORT &&
phba->sli4_hba.pc_sli4_params.mi_ver) { /* mi is only for the phyical port, no vports */ if (phba->link_flag & LS_CT_VEN_RPA) {
lpfc_printf_vlog(vport, KERN_INFO,
LOG_DISCOVERY | LOG_ELS |
LOG_CGN_MGMT, "6449 VEN RPA FDMI Success\n");
phba->link_flag &= ~LS_CT_VEN_RPA; break;
}
/* CGN is only for the physical port, no vports */ if (lpfc_fdmi_cmd(vport, ndlp, cmd,
LPFC_FDMI_VENDOR_ATTR_mi) == 0)
phba->link_flag |= LS_CT_VEN_RPA;
lpfc_printf_log(phba, KERN_INFO,
LOG_DISCOVERY | LOG_ELS, "6458 Send MI FDMI:%x Flag x%x\n",
phba->sli4_hba.pc_sli4_params.mi_ver,
phba->link_flag);
} else {
lpfc_printf_log(phba, KERN_INFO,
LOG_DISCOVERY | LOG_ELS, "6459 No FDMI VEN MI support - " "RPA Success\n");
} break;
} return;
}
/** * lpfc_fdmi_change_check - Check for changed FDMI parameters * @vport: pointer to a host virtual N_Port data structure. * * Check how many mapped NPorts we are connected to * Check if our hostname changed * Called from hbeat timeout routine to check if any FDMI parameters * changed. If so, re-register those Attributes.
*/ void
lpfc_fdmi_change_check(struct lpfc_vport *vport)
{ struct lpfc_hba *phba = vport->phba; struct lpfc_nodelist *ndlp;
uint16_t cnt;
if (!lpfc_is_link_up(phba)) return;
/* Must be connected to a Fabric */ if (!test_bit(FC_FABRIC, &vport->fc_flag)) return;
ndlp = lpfc_findnode_did(vport, FDMI_DID); if (!ndlp) return;
/* Check if system hostname changed */ if (strcmp(phba->os_host_name, init_utsname()->nodename)) {
memset(phba->os_host_name, 0, sizeof(phba->os_host_name));
scnprintf(phba->os_host_name, sizeof(phba->os_host_name), "%s",
init_utsname()->nodename);
lpfc_ns_cmd(vport, SLI_CTNS_RSNN_NN, 0, 0);
/* Since this effects multiple HBA and PORT attributes, we need * de-register and go thru the whole FDMI registration cycle. * DHBA -> DPRT -> RHBA -> RPA (physical port) * DPRT -> RPRT (vports)
*/ if (vport->port_type == LPFC_PHYSICAL_PORT) { /* For extra Vendor RPA */
phba->link_flag &= ~LS_CT_VEN_RPA;
lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA, 0);
} else {
ndlp = lpfc_findnode_did(phba->pport, FDMI_DID); if (!ndlp) return;
lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DPRT, 0);
}
/* Since this code path registers all the port attributes * we can just return without further checking.
*/ return;
}
if (!(vport->fdmi_port_mask & LPFC_FDMI_PORT_ATTR_num_disc)) return;
/* Check if the number of mapped NPorts changed */
cnt = lpfc_find_map_node(vport); if (cnt == vport->fdmi_num_disc) return;
if (typemask & ATTR_FC4_FCP)
ae->value_types[2] = 0x01; /* Type 0x8 - FCP */
if (typemask & ATTR_FC4_CT)
ae->value_types[7] = 0x01; /* Type 0x20 - CT */
if (typemask & ATTR_FC4_NVME)
ae->value_types[6] = 0x01; /* Type 0x28 - NVME */
return size;
}
/* Routines for all individual HBA attributes */ staticint
lpfc_fdmi_hba_attr_wwnn(struct lpfc_vport *vport, void *attr)
{ return lpfc_fdmi_set_attr_wwn(attr, RHBA_NODENAME,
&vport->fc_sparam.nodeName);
}
staticint
lpfc_fdmi_hba_attr_manufacturer(struct lpfc_vport *vport, void *attr)
{ /* This string MUST be consistent with other FC platforms * supported by Broadcom.
*/ return lpfc_fdmi_set_attr_string(attr, RHBA_MANUFACTURER, "Emulex Corporation");
}
staticint
lpfc_fdmi_hba_attr_num_ports(struct lpfc_vport *vport, void *attr)
{ /* Each driver instance corresponds to a single port */ return lpfc_fdmi_set_attr_u32(attr, RHBA_NUM_PORTS, 1);
}
/* Check to see if Firmware supports NVME and on physical port */ if ((phba->sli_rev == LPFC_SLI_REV4) && (vport == phba->pport) &&
phba->sli4_hba.pc_sli4_params.nvme)
fc4types |= ATTR_FC4_NVME;
/* Next fill in the specific FDMI cmd information */ switch (cmdcode) { case SLI_MGMT_RHAT: case SLI_MGMT_RHBA:
rh = (struct lpfc_fdmi_reg_hba *)&CtReq->un; /* HBA Identifier */
memcpy(&rh->hi.PortName, &phba->pport->fc_sparam.portName, sizeof(struct lpfc_name));
size += sizeof(struct lpfc_fdmi_hba_ident);
if (cmdcode == SLI_MGMT_RHBA) { /* Registered Port List */ /* One entry (port) per adapter */
rh->rpl.EntryCnt = cpu_to_be32(1);
memcpy(&rh->rpl.pe.PortName,
&phba->pport->fc_sparam.portName, sizeof(struct lpfc_name));
size += sizeof(struct lpfc_fdmi_reg_port_list);
}
ab = (struct lpfc_fdmi_attr_block *)((uint8_t *)rh + size);
ab->EntryCnt = 0;
size += FOURBYTES; /* add length of EntryCnt field */
/* Mask will dictate what attributes to build in the request */ while (mask) { if (mask & 0x1) {
func = lpfc_fdmi_hba_action[bit_pos];
addsz = func(vport, ((uint8_t *)rh + size)); if (addsz) {
ab->EntryCnt++;
size += addsz;
} /* check if another attribute fits */ if ((size + FDMI_MAX_ATTRLEN) >
(LPFC_BPL_SIZE - LPFC_CT_PREAMBLE)) goto hba_out;
}
mask = mask >> 1;
bit_pos++;
}
hba_out:
ab->EntryCnt = cpu_to_be32(ab->EntryCnt); /* Total size */
size += GID_REQUEST_SZ - 4; break;
case SLI_MGMT_RPRT: if (vport->port_type != LPFC_PHYSICAL_PORT) {
ndlp = lpfc_findnode_did(phba->pport, FDMI_DID); if (!ndlp) return 0;
}
fallthrough; case SLI_MGMT_RPA: /* Store base ptr right after preamble */
base = (struct lpfc_fdmi_reg_portattr *)&CtReq->un;
/* * The lpfc_ct_cmd/lpfc_get_req shall increment ndlp reference count * to hold ndlp reference for the corresponding callback function.
*/ if (!lpfc_ct_cmd(vport, rq, rsp, ndlp, cmpl, rsp_size, 0)) return 0;
/** * lpfc_delayed_disc_tmo - Timeout handler for delayed discovery timer. * @t: Context object of the timer. * * This function set the WORKER_DELAYED_DISC_TMO flag and wake up * the worker thread.
**/ void
lpfc_delayed_disc_tmo(struct timer_list *t)
{ struct lpfc_vport *vport = timer_container_of(vport, t,
delayed_disc_tmo); struct lpfc_hba *phba = vport->phba;
uint32_t tmo_posted; unsignedlong iflag;
if (!tmo_posted)
lpfc_worker_wake_up(phba); return;
}
/** * lpfc_delayed_disc_timeout_handler - Function called by worker thread to * handle delayed discovery. * @vport: pointer to a host virtual N_Port data structure. * * This function start nport discovery of the vport.
**/ void
lpfc_delayed_disc_timeout_handler(struct lpfc_vport *vport)
{ if (!test_and_clear_bit(FC_DISC_DELAYED, &vport->fc_flag)) return;
switch (b4) { case 0:
c = 'N'; break; case 1:
c = 'A'; break; case 2:
c = 'B'; break; case 3:
c = 'X'; break; default:
c = 0; break;
}
b4 = (rev & 0x0000000f);
/* The lpfc_ct_cmd/lpfc_get_req shall increment ndlp reference count * to hold ndlp reference for the corresponding callback function.
*/ if (!lpfc_ct_cmd(vport, mp, bmp, ndlp, cmpl, rsp_size, retry)) return 0;
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.102Bemerkung:
(vorverarbeitet am 2026-04-25)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.