Commit 1c5b12f7 authored by James Smart's avatar James Smart Committed by Christoph Hellwig

Fix implicit logo and RSCN handling for NVMET

NVMET didn't have any RSCN handling at all and
would not execute implicit LOGO when receiving a PLOGI
from an rport that NVMET had in state UNMAPPED.

Clean up the logic in lpfc_nlp_state_cleanup for
initiators (FCP and NVME). NVMET should not respond to
RSCN including allocating new ndlps so this code was
conditionalized when nvmet_support is true.  The check
for NLP_RCV_PLOGI in lpfc_setup_disc_node was moved
below the check for nvmet_support to allow the NVMET
to recover initiator nodes correctly.  The implicit
logo was introduced with lpfc_rcv_plogi when NVMET gets
a PLOGI on an ndlp in UNMAPPED state.  The RSCN handling
was modified to not respond to an RSCN in NVMET.  Instead
NVMET sends a GID_FT and determines if an NVMEP_INITIATOR
it has is UNMAPPED but no longer in the zone membership.
Signed-off-by: default avatarDick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: default avatarJames Smart <james.smart@broadcom.com>
Reviewed-by: default avatarJohannes Thumshirn <jthumshirn@suse.de>
parent aeb3c817
...@@ -537,19 +537,53 @@ lpfc_prep_node_fc4type(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type) ...@@ -537,19 +537,53 @@ lpfc_prep_node_fc4type(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type)
} }
} }
static void
lpfc_ns_rsp_audit_did(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type)
{
struct lpfc_hba *phba = vport->phba;
struct lpfc_nodelist *ndlp = NULL;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
/*
* To conserve rpi's, filter out addresses for other
* vports on the same physical HBAs.
*/
if (Did != vport->fc_myDID &&
(!lpfc_find_vport_by_did(phba, Did) ||
vport->cfg_peer_port_login)) {
if (!phba->nvmet_support) {
/* FCPI/NVMEI path. Process Did */
lpfc_prep_node_fc4type(vport, Did, fc4_type);
return;
}
/* NVMET path. NVMET only cares about NVMEI nodes. */
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
if (ndlp->nlp_type != NLP_NVME_INITIATOR ||
ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)
continue;
spin_lock_irq(shost->host_lock);
if (ndlp->nlp_DID == Did)
ndlp->nlp_flag &= ~NLP_NVMET_RECOV;
else
ndlp->nlp_flag |= NLP_NVMET_RECOV;
spin_unlock_irq(shost->host_lock);
}
}
}
static int static int
lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint8_t fc4_type, lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint8_t fc4_type,
uint32_t Size) uint32_t Size)
{ {
struct lpfc_hba *phba = vport->phba;
struct lpfc_sli_ct_request *Response = struct lpfc_sli_ct_request *Response =
(struct lpfc_sli_ct_request *) mp->virt; (struct lpfc_sli_ct_request *) mp->virt;
struct lpfc_nodelist *ndlp = NULL;
struct lpfc_dmabuf *mlast, *next_mp; struct lpfc_dmabuf *mlast, *next_mp;
uint32_t *ctptr = (uint32_t *) & Response->un.gid.PortType; uint32_t *ctptr = (uint32_t *) & Response->un.gid.PortType;
uint32_t Did, CTentry; uint32_t Did, CTentry;
int Cnt; int Cnt;
struct list_head head; struct list_head head;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_nodelist *ndlp = NULL;
lpfc_set_disctmo(vport); lpfc_set_disctmo(vport);
vport->num_disc_nodes = 0; vport->num_disc_nodes = 0;
...@@ -574,19 +608,7 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint8_t fc4_type, ...@@ -574,19 +608,7 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint8_t fc4_type,
/* Get next DID from NameServer List */ /* Get next DID from NameServer List */
CTentry = *ctptr++; CTentry = *ctptr++;
Did = ((be32_to_cpu(CTentry)) & Mask_DID); Did = ((be32_to_cpu(CTentry)) & Mask_DID);
lpfc_ns_rsp_audit_did(vport, Did, fc4_type);
ndlp = NULL;
/*
* Check for rscn processing or not
* To conserve rpi's, filter out addresses for other
* vports on the same physical HBAs.
*/
if ((Did != vport->fc_myDID) &&
((lpfc_find_vport_by_did(phba, Did) == NULL) ||
vport->cfg_peer_port_login))
lpfc_prep_node_fc4type(vport, Did, fc4_type);
if (CTentry & (cpu_to_be32(SLI_CT_LAST_ENTRY))) if (CTentry & (cpu_to_be32(SLI_CT_LAST_ENTRY)))
goto nsout1; goto nsout1;
...@@ -596,6 +618,22 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint8_t fc4_type, ...@@ -596,6 +618,22 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint8_t fc4_type,
} }
/* 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 (!(ndlp->nlp_flag & NLP_NVMET_RECOV))
continue;
lpfc_disc_state_machine(vport, ndlp, NULL,
NLP_EVT_DEVICE_RECOVERY);
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_NVMET_RECOV;
spin_lock_irq(shost->host_lock);
}
}
nsout1: nsout1:
list_del(&head); list_del(&head);
return 0; return 0;
......
...@@ -157,6 +157,7 @@ struct lpfc_node_rrq { ...@@ -157,6 +157,7 @@ struct lpfc_node_rrq {
#define NLP_LOGO_SND 0x00000100 /* sent LOGO request for this entry */ #define NLP_LOGO_SND 0x00000100 /* sent LOGO request for this entry */
#define NLP_RNID_SND 0x00000400 /* sent RNID request for this entry */ #define NLP_RNID_SND 0x00000400 /* sent RNID request for this entry */
#define NLP_ELS_SND_MASK 0x000007e0 /* sent ELS request for this entry */ #define NLP_ELS_SND_MASK 0x000007e0 /* sent ELS request for this entry */
#define NLP_NVMET_RECOV 0x00001000 /* NVMET auditing node for recovery. */
#define NLP_DEFER_RM 0x00010000 /* Remove this ndlp if no longer used */ #define NLP_DEFER_RM 0x00010000 /* Remove this ndlp if no longer used */
#define NLP_DELAY_TMO 0x00020000 /* delay timeout is running for node */ #define NLP_DELAY_TMO 0x00020000 /* delay timeout is running for node */
#define NLP_NPR_2B_DISC 0x00040000 /* node is included in num_disc_nodes */ #define NLP_NPR_2B_DISC 0x00040000 /* node is included in num_disc_nodes */
......
...@@ -5861,8 +5861,11 @@ lpfc_rscn_recovery_check(struct lpfc_vport *vport) ...@@ -5861,8 +5861,11 @@ lpfc_rscn_recovery_check(struct lpfc_vport *vport)
(ndlp->nlp_state == NLP_STE_UNUSED_NODE) || (ndlp->nlp_state == NLP_STE_UNUSED_NODE) ||
!lpfc_rscn_payload_check(vport, ndlp->nlp_DID)) !lpfc_rscn_payload_check(vport, ndlp->nlp_DID))
continue; continue;
/* NVME Target mode does not do RSCN Recovery. */
if (vport->phba->nvmet_support) if (vport->phba->nvmet_support)
continue; continue;
lpfc_disc_state_machine(vport, ndlp, NULL, lpfc_disc_state_machine(vport, ndlp, NULL,
NLP_EVT_DEVICE_RECOVERY); NLP_EVT_DEVICE_RECOVERY);
lpfc_cancel_retry_delay_tmo(vport, ndlp); lpfc_cancel_retry_delay_tmo(vport, ndlp);
...@@ -6150,22 +6153,16 @@ lpfc_els_handle_rscn(struct lpfc_vport *vport) ...@@ -6150,22 +6153,16 @@ lpfc_els_handle_rscn(struct lpfc_vport *vport)
ndlp = lpfc_findnode_did(vport, NameServer_DID); ndlp = lpfc_findnode_did(vport, NameServer_DID);
if (ndlp && NLP_CHK_NODE_ACT(ndlp) if (ndlp && NLP_CHK_NODE_ACT(ndlp)
&& ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) { && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) {
/* Good ndlp, issue CT Request to NameServer */ /* Good ndlp, issue CT Request to NameServer. Need to
* know how many gidfts were issued. If none, then just
* flush the RSCN. Otherwise, the outstanding requests
* need to complete.
*/
vport->gidft_inp = 0; vport->gidft_inp = 0;
if (lpfc_issue_gidft(vport) == 0) if (lpfc_issue_gidft(vport) > 0)
/* Wait for NameServer query cmpl before we can
* continue
*/
return 1; return 1;
} else { } else {
/* If login to NameServer does not exist, issue one */ /* Nameserver login in question. Revalidate. */
/* Good status, issue PLOGI to NameServer */
ndlp = lpfc_findnode_did(vport, NameServer_DID);
if (ndlp && NLP_CHK_NODE_ACT(ndlp))
/* Wait for NameServer login cmpl before we can
continue */
return 1;
if (ndlp) { if (ndlp) {
ndlp = lpfc_enable_node(vport, ndlp, ndlp = lpfc_enable_node(vport, ndlp,
NLP_STE_PLOGI_ISSUE); NLP_STE_PLOGI_ISSUE);
......
...@@ -4148,7 +4148,6 @@ lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ...@@ -4148,7 +4148,6 @@ lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
int old_state, int new_state) int old_state, int new_state)
{ {
struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
if (new_state == NLP_STE_UNMAPPED_NODE) { if (new_state == NLP_STE_UNMAPPED_NODE) {
ndlp->nlp_flag &= ~NLP_NODEV_REMOVE; ndlp->nlp_flag &= ~NLP_NODEV_REMOVE;
...@@ -4167,14 +4166,14 @@ lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ...@@ -4167,14 +4166,14 @@ lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
lpfc_unregister_remote_port(ndlp); lpfc_unregister_remote_port(ndlp);
} }
/* Notify the NVME transport of this rport's loss */ /* Notify the NVME transport of this rport's loss on the
if (((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || * Initiator. For NVME Target, should upcall transport
(phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) && * in the else clause when API available.
(vport->phba->nvmet_support == 0) && */
((ndlp->nlp_fc4_type & NLP_FC4_NVME) || if (ndlp->nlp_fc4_type & NLP_FC4_NVME) {
(ndlp->nlp_DID == Fabric_DID))) {
vport->phba->nport_event_cnt++; vport->phba->nport_event_cnt++;
lpfc_nvme_unregister_port(vport, ndlp); if (vport->phba->nvmet_support == 0)
lpfc_nvme_unregister_port(vport, ndlp);
} }
} }
...@@ -5128,6 +5127,8 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) ...@@ -5128,6 +5127,8 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did)
ndlp = lpfc_findnode_did(vport, did); ndlp = lpfc_findnode_did(vport, did);
if (!ndlp) { if (!ndlp) {
if (vport->phba->nvmet_support)
return NULL;
if ((vport->fc_flag & FC_RSCN_MODE) != 0 && if ((vport->fc_flag & FC_RSCN_MODE) != 0 &&
lpfc_rscn_payload_check(vport, did) == 0) lpfc_rscn_payload_check(vport, did) == 0)
return NULL; return NULL;
...@@ -5135,56 +5136,73 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) ...@@ -5135,56 +5136,73 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did)
if (!ndlp) if (!ndlp)
return NULL; return NULL;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
if (vport->phba->nvmet_support)
return ndlp;
spin_lock_irq(shost->host_lock); spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_NPR_2B_DISC; ndlp->nlp_flag |= NLP_NPR_2B_DISC;
spin_unlock_irq(shost->host_lock); spin_unlock_irq(shost->host_lock);
return ndlp; return ndlp;
} else if (!NLP_CHK_NODE_ACT(ndlp)) { } else if (!NLP_CHK_NODE_ACT(ndlp)) {
if (vport->phba->nvmet_support)
return NULL;
ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_NPR_NODE); ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_NPR_NODE);
if (!ndlp) if (!ndlp)
return NULL; return NULL;
if (vport->phba->nvmet_support)
return ndlp;
spin_lock_irq(shost->host_lock); spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_NPR_2B_DISC; ndlp->nlp_flag |= NLP_NPR_2B_DISC;
spin_unlock_irq(shost->host_lock); spin_unlock_irq(shost->host_lock);
return ndlp; return ndlp;
} }
/* The NVME Target does not want to actively manage an rport.
* The goal is to allow the target to reset its state and clear
* pending IO in preparation for the initiator to recover.
*/
if ((vport->fc_flag & FC_RSCN_MODE) && if ((vport->fc_flag & FC_RSCN_MODE) &&
!(vport->fc_flag & FC_NDISC_ACTIVE)) { !(vport->fc_flag & FC_NDISC_ACTIVE)) {
if (lpfc_rscn_payload_check(vport, did)) { if (lpfc_rscn_payload_check(vport, did)) {
/* If we've already received a PLOGI from this NPort
* we don't need to try to discover it again.
*/
if (ndlp->nlp_flag & NLP_RCV_PLOGI)
return NULL;
/* Since this node is marked for discovery, /* Since this node is marked for discovery,
* delay timeout is not needed. * delay timeout is not needed.
*/ */
lpfc_cancel_retry_delay_tmo(vport, ndlp); lpfc_cancel_retry_delay_tmo(vport, ndlp);
/* NVME Target mode waits until rport is known to be
* impacted by the RSCN before it transitions. No
* active management - just go to NPR provided the
* node had a valid login.
*/
if (vport->phba->nvmet_support) if (vport->phba->nvmet_support)
return ndlp; return ndlp;
/* If we've already received a PLOGI from this NPort
* we don't need to try to discover it again.
*/
if (ndlp->nlp_flag & NLP_RCV_PLOGI)
return NULL;
spin_lock_irq(shost->host_lock); spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_NPR_2B_DISC; ndlp->nlp_flag |= NLP_NPR_2B_DISC;
spin_unlock_irq(shost->host_lock); spin_unlock_irq(shost->host_lock);
} else } else
ndlp = NULL; ndlp = NULL;
} else { } else {
/* If we've already received a PLOGI from this NPort, /* If the initiator received a PLOGI from this NPort or if the
* or we are already in the process of discovery on it, * initiator is already in the process of discovery on it,
* we don't need to try to discover it again. * there's no need to try to discover it again.
*/ */
if (ndlp->nlp_state == NLP_STE_ADISC_ISSUE || if (ndlp->nlp_state == NLP_STE_ADISC_ISSUE ||
ndlp->nlp_state == NLP_STE_PLOGI_ISSUE || ndlp->nlp_state == NLP_STE_PLOGI_ISSUE ||
ndlp->nlp_flag & NLP_RCV_PLOGI) (!vport->phba->nvmet_support &&
ndlp->nlp_flag & NLP_RCV_PLOGI))
return NULL; return NULL;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
if (vport->phba->nvmet_support) if (vport->phba->nvmet_support)
return ndlp; return ndlp;
/* Moving to NPR state clears unsolicited flags and
* allows for rediscovery
*/
lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
spin_lock_irq(shost->host_lock); spin_lock_irq(shost->host_lock);
ndlp->nlp_flag |= NLP_NPR_2B_DISC; ndlp->nlp_flag |= NLP_NPR_2B_DISC;
spin_unlock_irq(shost->host_lock); spin_unlock_irq(shost->host_lock);
......
...@@ -361,8 +361,12 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ...@@ -361,8 +361,12 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
case NLP_STE_PRLI_ISSUE: case NLP_STE_PRLI_ISSUE:
case NLP_STE_UNMAPPED_NODE: case NLP_STE_UNMAPPED_NODE:
case NLP_STE_MAPPED_NODE: case NLP_STE_MAPPED_NODE:
/* lpfc_plogi_confirm_nport skips fabric did, handle it here */ /* For initiators, lpfc_plogi_confirm_nport skips fabric did.
if (!(ndlp->nlp_type & NLP_FABRIC)) { * For target mode, execute implicit logo.
* Fabric nodes go into NPR.
*/
if (!(ndlp->nlp_type & NLP_FABRIC) &&
!(phba->nvmet_support)) {
lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb,
ndlp, NULL); ndlp, NULL);
return 1; return 1;
......
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