lpfc: add support to generate RSCN events for nport
authorJames Smart <jsmart2021@gmail.com>
Tue, 14 May 2019 21:58:05 +0000 (14:58 -0700)
committerChristoph Hellwig <hch@lst.de>
Fri, 21 Jun 2019 09:08:37 +0000 (11:08 +0200)
This patch adds general RSCN support:

 - The ability to transmit an RSCN to the port on the other end of
   the link (regular port if pt2pt, or fabric controller if fabric).
 - And general recognition of an RSCN ELS when an ELS is received.

Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>
Reviewed-by: Hannes Reinecke <hare@suse.com>
Reviewed-by: Arun Easi <aeasi@marvell.com>
Signed-off-by: Sagi Grimberg <sagi@grimberg.me>
Signed-off-by: Christoph Hellwig <hch@lst.de>
drivers/scsi/lpfc/lpfc.h
drivers/scsi/lpfc/lpfc_crtn.h
drivers/scsi/lpfc/lpfc_els.c
drivers/scsi/lpfc/lpfc_hbadisc.c
drivers/scsi/lpfc/lpfc_hw.h
drivers/scsi/lpfc/lpfc_sli.c

index aafcffaa25f716ac33cfab2a3ce884ad5d7b8d83..14293c5467726417612c5e7e4b822d34fffba115 100644 (file)
@@ -274,6 +274,7 @@ struct lpfc_stats {
        uint32_t elsXmitADISC;
        uint32_t elsXmitLOGO;
        uint32_t elsXmitSCR;
+       uint32_t elsXmitRSCN;
        uint32_t elsXmitRNID;
        uint32_t elsXmitFARP;
        uint32_t elsXmitFARPR;
index e0b14d791b8c243d1a87975f08efc88909b2cc2e..4b8eb9107b859208c077afcbfc228c32fde61cc4 100644 (file)
@@ -141,6 +141,7 @@ int lpfc_issue_els_adisc(struct lpfc_vport *, struct lpfc_nodelist *, uint8_t);
 int lpfc_issue_els_logo(struct lpfc_vport *, struct lpfc_nodelist *, uint8_t);
 int lpfc_issue_els_npiv_logo(struct lpfc_vport *, struct lpfc_nodelist *);
 int lpfc_issue_els_scr(struct lpfc_vport *, uint32_t, uint8_t);
+int lpfc_issue_els_rscn(struct lpfc_vport *vport, uint8_t retry);
 int lpfc_issue_fabric_reglogin(struct lpfc_vport *);
 int lpfc_els_free_iocb(struct lpfc_hba *, struct lpfc_iocbq *);
 int lpfc_ct_free_iocb(struct lpfc_hba *, struct lpfc_iocbq *);
@@ -355,6 +356,7 @@ void lpfc_mbox_timeout_handler(struct lpfc_hba *);
 struct lpfc_nodelist *lpfc_findnode_did(struct lpfc_vport *, uint32_t);
 struct lpfc_nodelist *lpfc_findnode_wwpn(struct lpfc_vport *,
                                         struct lpfc_name *);
+struct lpfc_nodelist *lpfc_findnode_mapped(struct lpfc_vport *vport);
 
 int lpfc_sli_issue_mbox_wait(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
 
index 5ac4f8d76b91daee26a11fd7ad36a061a1869145..00f5d9d547f9e90d2db135675e8de3c02d968d74 100644 (file)
@@ -30,6 +30,8 @@
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport_fc.h>
+#include <uapi/scsi/fc/fc_fs.h>
+#include <uapi/scsi/fc/fc_els.h>
 
 #include "lpfc_hw4.h"
 #include "lpfc_hw.h"
@@ -3078,6 +3080,116 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry)
        return 0;
 }
 
+/**
+ * lpfc_issue_els_rscn - Issue an RSCN to the Fabric Controller (Fabric)
+ *   or the other nport (pt2pt).
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @retry: number of retries to the command IOCB.
+ *
+ * This routine issues a RSCN to the Fabric Controller (DID 0xFFFFFD)
+ *  when connected to a fabric, or to the remote port when connected
+ *  in point-to-point mode. When sent to the Fabric Controller, it will
+ *  replay the RSCN to registered recipients.
+ *
+ * Note that, in lpfc_prep_els_iocb() routine, the reference count of ndlp
+ * will be incremented by 1 for holding the ndlp and the reference to ndlp
+ * will be stored into the context1 field of the IOCB for the completion
+ * callback function to the RSCN ELS command.
+ *
+ * Return code
+ *   0 - Successfully issued RSCN command
+ *   1 - Failed to issue RSCN command
+ **/
+int
+lpfc_issue_els_rscn(struct lpfc_vport *vport, uint8_t retry)
+{
+       struct lpfc_hba *phba = vport->phba;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_nodelist *ndlp;
+       struct {
+               struct fc_els_rscn rscn;
+               struct fc_els_rscn_page portid;
+       } *event;
+       uint32_t nportid;
+       uint16_t cmdsize = sizeof(*event);
+
+       /* Not supported for private loop */
+       if (phba->fc_topology == LPFC_TOPOLOGY_LOOP &&
+           !(vport->fc_flag & FC_PUBLIC_LOOP))
+               return 1;
+
+       if (vport->fc_flag & FC_PT2PT) {
+               /* find any mapped nport - that would be the other nport */
+               ndlp = lpfc_findnode_mapped(vport);
+               if (!ndlp)
+                       return 1;
+       } else {
+               nportid = FC_FID_FCTRL;
+               /* find the fabric controller node */
+               ndlp = lpfc_findnode_did(vport, nportid);
+               if (!ndlp) {
+                       /* if one didn't exist, make one */
+                       ndlp = lpfc_nlp_init(vport, nportid);
+                       if (!ndlp)
+                               return 1;
+                       lpfc_enqueue_node(vport, ndlp);
+               } else if (!NLP_CHK_NODE_ACT(ndlp)) {
+                       ndlp = lpfc_enable_node(vport, ndlp,
+                                               NLP_STE_UNUSED_NODE);
+                       if (!ndlp)
+                               return 1;
+               }
+       }
+
+       elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp,
+                                    ndlp->nlp_DID, ELS_CMD_RSCN_XMT);
+
+       if (!elsiocb) {
+               /* This will trigger the release of the node just
+                * allocated
+                */
+               lpfc_nlp_put(ndlp);
+               return 1;
+       }
+
+       event = ((struct lpfc_dmabuf *)elsiocb->context2)->virt;
+
+       event->rscn.rscn_cmd = ELS_RSCN;
+       event->rscn.rscn_page_len = sizeof(struct fc_els_rscn_page);
+       event->rscn.rscn_plen = cpu_to_be16(cmdsize);
+
+       nportid = vport->fc_myDID;
+       /* appears that page flags must be 0 for fabric to broadcast RSCN */
+       event->portid.rscn_page_flags = 0;
+       event->portid.rscn_fid[0] = (nportid & 0x00FF0000) >> 16;
+       event->portid.rscn_fid[1] = (nportid & 0x0000FF00) >> 8;
+       event->portid.rscn_fid[2] = nportid & 0x000000FF;
+
+       lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
+                             "Issue RSCN:       did:x%x",
+                             ndlp->nlp_DID, 0, 0);
+
+       phba->fc_stat.elsXmitRSCN++;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd;
+       if (lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0) ==
+           IOCB_ERROR) {
+               /* The additional lpfc_nlp_put will cause the following
+                * lpfc_els_free_iocb routine to trigger the rlease of
+                * the node.
+                */
+               lpfc_nlp_put(ndlp);
+               lpfc_els_free_iocb(phba, elsiocb);
+               return 1;
+       }
+       /* This will cause the callback-function lpfc_cmpl_els_cmd to
+        * trigger the release of node.
+        */
+       if (!(vport->fc_flag & FC_PT2PT))
+               lpfc_nlp_put(ndlp);
+
+       return 0;
+}
+
 /**
  * lpfc_issue_els_farpr - Issue a farp to an node on a vport
  * @vport: pointer to a host virtual N_Port data structure.
@@ -6318,6 +6430,16 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
                fc_host_post_event(shost, fc_get_event_number(),
                        FCH_EVT_RSCN, lp[i]);
 
+       /* Check if RSCN is coming from a direct-connected remote NPort */
+       if (vport->fc_flag & FC_PT2PT) {
+               /* If so, just ACC it, no other action needed for now */
+               lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+                                "2024 pt2pt RSCN %08x Data: x%x x%x\n",
+                                *lp, vport->fc_flag, payload_len);
+               lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
+               return 0;
+       }
+
        /* If we are about to begin discovery, just ACC the RSCN.
         * Discovery processing will satisfy it.
         */
index c43852f97f2530f4fe5bee4578960b449664777c..28ecaa7fc715ba900b85888df66a17ba2a9acac3 100644 (file)
@@ -5276,6 +5276,41 @@ lpfc_findnode_did(struct lpfc_vport *vport, uint32_t did)
        return ndlp;
 }
 
+struct lpfc_nodelist *
+lpfc_findnode_mapped(struct lpfc_vport *vport)
+{
+       struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+       struct lpfc_nodelist *ndlp;
+       uint32_t data1;
+       unsigned long iflags;
+
+       spin_lock_irqsave(shost->host_lock, iflags);
+
+       list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
+               if (ndlp->nlp_state == NLP_STE_UNMAPPED_NODE ||
+                   ndlp->nlp_state == NLP_STE_MAPPED_NODE) {
+                       data1 = (((uint32_t)ndlp->nlp_state << 24) |
+                                ((uint32_t)ndlp->nlp_xri << 16) |
+                                ((uint32_t)ndlp->nlp_type << 8) |
+                                ((uint32_t)ndlp->nlp_rpi & 0xff));
+                       spin_unlock_irqrestore(shost->host_lock, iflags);
+                       lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
+                                        "2025 FIND node DID "
+                                        "Data: x%p x%x x%x x%x %p\n",
+                                        ndlp, ndlp->nlp_DID,
+                                        ndlp->nlp_flag, data1,
+                                        ndlp->active_rrqs_xri_bitmap);
+                       return ndlp;
+               }
+       }
+       spin_unlock_irqrestore(shost->host_lock, iflags);
+
+       /* FIND node did <did> NOT FOUND */
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE,
+                        "2026 FIND mapped did NOT FOUND.\n");
+       return NULL;
+}
+
 struct lpfc_nodelist *
 lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did)
 {
index edd8f398202363b5dc133c77980f96b064f18e74..5b439a6dcde15601cecddd1a9bf1c28ea9118109 100644 (file)
@@ -601,6 +601,7 @@ struct fc_vft_header {
 #define ELS_CMD_RPL       0x57000000
 #define ELS_CMD_FAN       0x60000000
 #define ELS_CMD_RSCN      0x61040000
+#define ELS_CMD_RSCN_XMT  0x61040008
 #define ELS_CMD_SCR       0x62000000
 #define ELS_CMD_RNID      0x78000000
 #define ELS_CMD_LIRR      0x7A000000
@@ -642,6 +643,7 @@ struct fc_vft_header {
 #define ELS_CMD_RPL       0x57
 #define ELS_CMD_FAN       0x60
 #define ELS_CMD_RSCN      0x0461
+#define ELS_CMD_RSCN_XMT  0x08000461
 #define ELS_CMD_SCR       0x62
 #define ELS_CMD_RNID      0x78
 #define ELS_CMD_LIRR      0x7A
index d1512e4f9791732acc225f234024630d8ec4e0c2..4329cc44bb55ccf86f8dd5854dc7f1cad2b61dbd 100644 (file)
@@ -9398,6 +9398,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
                if (if_type >= LPFC_SLI_INTF_IF_TYPE_2) {
                        if (pcmd && (*pcmd == ELS_CMD_FLOGI ||
                                *pcmd == ELS_CMD_SCR ||
+                               *pcmd == ELS_CMD_RSCN_XMT ||
                                *pcmd == ELS_CMD_FDISC ||
                                *pcmd == ELS_CMD_LOGO ||
                                *pcmd == ELS_CMD_PLOGI)) {