]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
scsi: lpfc: Fix unsolicited FLOGI kref imbalance when in direct attached topology
authorJustin Tee <justin.tee@broadcom.com>
Fri, 26 Jul 2024 23:15:09 +0000 (16:15 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Sat, 3 Aug 2024 01:48:05 +0000 (21:48 -0400)
In direct attached topology, certain target vendors that are quick to issue
FLOGI followed by a cable pull for more than dev_loss_tmo may result in a
kref imbalance for the remote port ndlp object.

Add an nlp_get when the defer_flogi_acc flag is set.  This is expected to
balance the nlp_put in the defer_flogi_acc clause in the
lpfc_issue_els_flogi() routine.  Because we need to retain the ndlp ptr,
reorganize all of the defer_flogi_acc information into one
lpfc_defer_flogi_acc struct.

Signed-off-by: Justin Tee <justin.tee@broadcom.com>
Link: https://lore.kernel.org/r/20240726231512.92867-6-justintee8345@gmail.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/lpfc/lpfc.h
drivers/scsi/lpfc/lpfc_els.c
drivers/scsi/lpfc/lpfc_hbadisc.c

index 7c147d6ea8a8ff5ab61449743080fc975490404f..e5a9c5a323f8be67be0d05aa5ccb95d662d1c626 100644 (file)
@@ -306,6 +306,14 @@ struct lpfc_stats {
 
 struct lpfc_hba;
 
+/* Data structure to keep withheld FLOGI_ACC information */
+struct lpfc_defer_flogi_acc {
+       bool flag;
+       u16 rx_id;
+       u16 ox_id;
+       struct lpfc_nodelist *ndlp;
+
+};
 
 #define LPFC_VMID_TIMER   300  /* timer interval in seconds */
 
@@ -1430,9 +1438,7 @@ struct lpfc_hba {
        uint16_t vlan_id;
        struct list_head fcf_conn_rec_list;
 
-       bool defer_flogi_acc_flag;
-       uint16_t defer_flogi_acc_rx_id;
-       uint16_t defer_flogi_acc_ox_id;
+       struct lpfc_defer_flogi_acc defer_flogi_acc;
 
        spinlock_t ct_ev_lock; /* synchronize access to ct_ev_waiters */
        struct list_head ct_ev_waiters;
index 6d49e23f6a628741c3bcf4dbff9de5bffe990975..b5a8d050419afefb8c5e92dd4155d5afd490bc7a 100644 (file)
@@ -1392,7 +1392,7 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        phba->link_flag &= ~LS_EXTERNAL_LOOPBACK;
 
        /* Check for a deferred FLOGI ACC condition */
-       if (phba->defer_flogi_acc_flag) {
+       if (phba->defer_flogi_acc.flag) {
                /* lookup ndlp for received FLOGI */
                ndlp = lpfc_findnode_did(vport, 0);
                if (!ndlp)
@@ -1406,34 +1406,38 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
                if (phba->sli_rev == LPFC_SLI_REV4) {
                        bf_set(wqe_ctxt_tag,
                               &defer_flogi_acc.wqe.xmit_els_rsp.wqe_com,
-                              phba->defer_flogi_acc_rx_id);
+                              phba->defer_flogi_acc.rx_id);
                        bf_set(wqe_rcvoxid,
                               &defer_flogi_acc.wqe.xmit_els_rsp.wqe_com,
-                              phba->defer_flogi_acc_ox_id);
+                              phba->defer_flogi_acc.ox_id);
                } else {
                        icmd = &defer_flogi_acc.iocb;
-                       icmd->ulpContext = phba->defer_flogi_acc_rx_id;
+                       icmd->ulpContext = phba->defer_flogi_acc.rx_id;
                        icmd->unsli3.rcvsli3.ox_id =
-                               phba->defer_flogi_acc_ox_id;
+                               phba->defer_flogi_acc.ox_id;
                }
 
                lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
                                 "3354 Xmit deferred FLOGI ACC: rx_id: x%x,"
                                 " ox_id: x%x, hba_flag x%lx\n",
-                                phba->defer_flogi_acc_rx_id,
-                                phba->defer_flogi_acc_ox_id, phba->hba_flag);
+                                phba->defer_flogi_acc.rx_id,
+                                phba->defer_flogi_acc.ox_id, phba->hba_flag);
 
                /* Send deferred FLOGI ACC */
                lpfc_els_rsp_acc(vport, ELS_CMD_FLOGI, &defer_flogi_acc,
                                 ndlp, NULL);
 
-               phba->defer_flogi_acc_flag = false;
-               vport->fc_myDID = did;
+               phba->defer_flogi_acc.flag = false;
 
-               /* Decrement ndlp reference count to indicate the node can be
-                * released when other references are removed.
+               /* Decrement the held ndlp that was incremented when the
+                * deferred flogi acc flag was set.
                 */
-               lpfc_nlp_put(ndlp);
+               if (phba->defer_flogi_acc.ndlp) {
+                       lpfc_nlp_put(phba->defer_flogi_acc.ndlp);
+                       phba->defer_flogi_acc.ndlp = NULL;
+               }
+
+               vport->fc_myDID = did;
        }
 
        return 0;
@@ -8456,9 +8460,9 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
 
        /* Defer ACC response until AFTER we issue a FLOGI */
        if (!test_bit(HBA_FLOGI_ISSUED, &phba->hba_flag)) {
-               phba->defer_flogi_acc_rx_id = bf_get(wqe_ctxt_tag,
+               phba->defer_flogi_acc.rx_id = bf_get(wqe_ctxt_tag,
                                                     &wqe->xmit_els_rsp.wqe_com);
-               phba->defer_flogi_acc_ox_id = bf_get(wqe_rcvoxid,
+               phba->defer_flogi_acc.ox_id = bf_get(wqe_rcvoxid,
                                                     &wqe->xmit_els_rsp.wqe_com);
 
                vport->fc_myDID = did;
@@ -8466,11 +8470,17 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
                lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
                                 "3344 Deferring FLOGI ACC: rx_id: x%x,"
                                 " ox_id: x%x, hba_flag x%lx\n",
-                                phba->defer_flogi_acc_rx_id,
-                                phba->defer_flogi_acc_ox_id, phba->hba_flag);
+                                phba->defer_flogi_acc.rx_id,
+                                phba->defer_flogi_acc.ox_id, phba->hba_flag);
 
-               phba->defer_flogi_acc_flag = true;
+               phba->defer_flogi_acc.flag = true;
 
+               /* This nlp_get is paired with nlp_puts that reset the
+                * defer_flogi_acc.flag back to false.  We need to retain
+                * a kref on the ndlp until the deferred FLOGI ACC is
+                * processed or cancelled.
+                */
+               phba->defer_flogi_acc.ndlp = lpfc_nlp_get(ndlp);
                return 0;
        }
 
@@ -10506,7 +10516,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
 
                lpfc_els_rcv_flogi(vport, elsiocb, ndlp);
                /* retain node if our response is deferred */
-               if (phba->defer_flogi_acc_flag)
+               if (phba->defer_flogi_acc.flag)
                        break;
                if (newnode)
                        lpfc_disc_state_machine(vport, ndlp, NULL,
index f21c5993e8d72ea44fab876c7134458ec5465865..35c9181c6608a52db6c025168da8d2a3c53bb15c 100644 (file)
@@ -1255,7 +1255,14 @@ lpfc_linkdown(struct lpfc_hba *phba)
        lpfc_scsi_dev_block(phba);
        offline = pci_channel_offline(phba->pcidev);
 
-       phba->defer_flogi_acc_flag = false;
+       /* Decrement the held ndlp if there is a deferred flogi acc */
+       if (phba->defer_flogi_acc.flag) {
+               if (phba->defer_flogi_acc.ndlp) {
+                       lpfc_nlp_put(phba->defer_flogi_acc.ndlp);
+                       phba->defer_flogi_acc.ndlp = NULL;
+               }
+       }
+       phba->defer_flogi_acc.flag = false;
 
        /* Clear external loopback plug detected flag */
        phba->link_flag &= ~LS_EXTERNAL_LOOPBACK;
@@ -1377,7 +1384,7 @@ lpfc_linkup_port(struct lpfc_vport *vport)
                (vport != phba->pport))
                return;
 
-       if (phba->defer_flogi_acc_flag) {
+       if (phba->defer_flogi_acc.flag) {
                clear_bit(FC_ABORT_DISCOVERY, &vport->fc_flag);
                clear_bit(FC_RSCN_MODE, &vport->fc_flag);
                clear_bit(FC_NLP_MORE, &vport->fc_flag);