scsi: lpfc: Fix RSCN timeout due to incorrect gidft counter
authorDick Kennedy <dick.kennedy@broadcom.com>
Mon, 3 Aug 2020 21:02:24 +0000 (14:02 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 5 Aug 2020 00:56:57 +0000 (20:56 -0400)
In configs with a large number of initiators in the same zone (>250), RSCN
timeouts are seen when creating or deleting vports:

   lpfc 0000:07:00.1: 5:(0):0231 RSCN timeout Data: x0 x3

During RSCN processing driver issues GID_FT command to nameserver. A
counter for number of simultaneous GID_FT commands is maintained (an
unsigned value). The counter is incremented when the GID_FT is issued.  If
the GID_FT command fails for some reason the driver retries the GID_FT from
the completion call back. But the counter was decremented before the retry
was issued.  When the second GID_FT completes, the callback again tries to
decrement the counter, possibly wrapping to a very large non-zero value,
which causes the RSCN cleanup code to not execute. Thus the RSCN timeout
failure.

Do not decrement the counter on a retry. Also add defensive checks to
ensure the counter is not decremented if already zero.

Link: https://lore.kernel.org/r/20200803210229.23063-4-jsmart2021@gmail.com
Signed-off-by: Dick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/lpfc/lpfc_ct.c

index dd9f2bf..ef2015f 100644 (file)
@@ -713,7 +713,8 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                /* This is a GID_FT completing so the gidft_inp counter was
                 * incremented before the GID_FT was issued to the wire.
                 */
-               vport->gidft_inp--;
+               if (vport->gidft_inp)
+                       vport->gidft_inp--;
 
                /*
                 * Skip processing the NS response
@@ -741,11 +742,14 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                                goto out;
 
                        /* CT command is being retried */
-                       vport->gidft_inp--;
                        rc = lpfc_ns_cmd(vport, SLI_CTNS_GID_FT,
                                         vport->fc_ns_retry, type);
                        if (rc == 0)
                                goto out;
+                       else { /* Unable to send NS cmd */
+                               if (vport->gidft_inp)
+                                       vport->gidft_inp--;
+                       }
                }
                if (vport->fc_flag & FC_RSCN_MODE)
                        lpfc_els_flush_rscn(vport);
@@ -825,7 +829,8 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                                (uint32_t) CTrsp->ReasonCode,
                                (uint32_t) CTrsp->Explanation);
                }
-               vport->gidft_inp--;
+               if (vport->gidft_inp)
+                       vport->gidft_inp--;
        }
 
        lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
@@ -918,7 +923,8 @@ lpfc_cmpl_ct_cmd_gid_pt(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                /* This is a GID_PT completing so the gidft_inp counter was
                 * incremented before the GID_PT was issued to the wire.
                 */
-               vport->gidft_inp--;
+               if (vport->gidft_inp)
+                       vport->gidft_inp--;
 
                /*
                 * Skip processing the NS response
@@ -942,11 +948,14 @@ lpfc_cmpl_ct_cmd_gid_pt(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                                vport->fc_ns_retry++;
 
                        /* CT command is being retried */
-                       vport->gidft_inp--;
                        rc = lpfc_ns_cmd(vport, SLI_CTNS_GID_PT,
                                         vport->fc_ns_retry, GID_PT_N_PORT);
                        if (rc == 0)
                                goto out;
+                       else { /* Unable to send NS cmd */
+                               if (vport->gidft_inp)
+                                       vport->gidft_inp--;
+                       }
                }
                if (vport->fc_flag & FC_RSCN_MODE)
                        lpfc_els_flush_rscn(vport);
@@ -1027,7 +1036,8 @@ lpfc_cmpl_ct_cmd_gid_pt(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                                (uint32_t)CTrsp->ReasonCode,
                                (uint32_t)CTrsp->Explanation);
                }
-               vport->gidft_inp--;
+               if (vport->gidft_inp)
+                       vport->gidft_inp--;
        }
 
        lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,