scsi: lpfc: Fix host hang at boot or slow boot
authorJames Smart <jsmart2021@gmail.com>
Sun, 22 Sep 2019 03:58:58 +0000 (20:58 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 1 Oct 2019 02:07:10 +0000 (22:07 -0400)
Scenarios were seen where a host hung when the system booted or the host
was very slow in booting. The link would not come up and no luns were
visible to the host.

After investigation, this was found to be due to the introduction of a new
ACQE that adapter may generate to report a adapter hw warning. The ACQE was
delivered to the driver very early in adapter initialization, when the
driver did not expect command completion. As part of handling this
unexpected interrupt the an EQEs are consumed and discarded and the EQ
rearmed. The issue is the CQ that cause the EQE and thus the interrupt was
not processed and the CQ was left unarmed. Meaning it would no longer
generate a new interrupt condition. Subsequent mailbox commands used to
initialize the adapter use the same CQ, and as there was no completion
interrupt generated, the driver never saw the mailbox commands complete and
it would wait long command timeouts.

Fix by having the early flush routine also process the related CQ and rearm
the CQ.

Link: https://lore.kernel.org/r/20190922035906.10977-13-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_sli.c

index 939efee..412cd8c 100644 (file)
@@ -87,6 +87,10 @@ static void lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba,
                                     struct lpfc_eqe *eqe);
 static bool lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba);
 static bool lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba);
+static struct lpfc_cqe *lpfc_sli4_cq_get(struct lpfc_queue *q);
+static void __lpfc_sli4_consume_cqe(struct lpfc_hba *phba,
+                                   struct lpfc_queue *cq,
+                                   struct lpfc_cqe *cqe);
 
 static IOCB_t *
 lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq)
@@ -467,21 +471,47 @@ __lpfc_sli4_consume_eqe(struct lpfc_hba *phba, struct lpfc_queue *eq,
 }
 
 static void
-lpfc_sli4_eq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq)
+lpfc_sli4_eqcq_flush(struct lpfc_hba *phba, struct lpfc_queue *eq)
 {
-       struct lpfc_eqe *eqe;
-       uint32_t count = 0;
+       struct lpfc_eqe *eqe = NULL;
+       u32 eq_count = 0, cq_count = 0;
+       struct lpfc_cqe *cqe = NULL;
+       struct lpfc_queue *cq = NULL, *childq = NULL;
+       int cqid = 0;
 
        /* walk all the EQ entries and drop on the floor */
        eqe = lpfc_sli4_eq_get(eq);
        while (eqe) {
+               /* Get the reference to the corresponding CQ */
+               cqid = bf_get_le32(lpfc_eqe_resource_id, eqe);
+               cq = NULL;
+
+               list_for_each_entry(childq, &eq->child_list, list) {
+                       if (childq->queue_id == cqid) {
+                               cq = childq;
+                               break;
+                       }
+               }
+               /* If CQ is valid, iterate through it and drop all the CQEs */
+               if (cq) {
+                       cqe = lpfc_sli4_cq_get(cq);
+                       while (cqe) {
+                               __lpfc_sli4_consume_cqe(phba, cq, cqe);
+                               cq_count++;
+                               cqe = lpfc_sli4_cq_get(cq);
+                       }
+                       /* Clear and re-arm the CQ */
+                       phba->sli4_hba.sli4_write_cq_db(phba, cq, cq_count,
+                           LPFC_QUEUE_REARM);
+                       cq_count = 0;
+               }
                __lpfc_sli4_consume_eqe(phba, eq, eqe);
-               count++;
+               eq_count++;
                eqe = lpfc_sli4_eq_get(eq);
        }
 
        /* Clear and re-arm the EQ */
-       phba->sli4_hba.sli4_write_eq_db(phba, eq, count, LPFC_QUEUE_REARM);
+       phba->sli4_hba.sli4_write_eq_db(phba, eq, eq_count, LPFC_QUEUE_REARM);
 }
 
 static int
@@ -14236,7 +14266,7 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id)
                spin_lock_irqsave(&phba->hbalock, iflag);
                if (phba->link_state < LPFC_LINK_DOWN)
                        /* Flush, clear interrupt, and rearm the EQ */
-                       lpfc_sli4_eq_flush(phba, fpeq);
+                       lpfc_sli4_eqcq_flush(phba, fpeq);
                spin_unlock_irqrestore(&phba->hbalock, iflag);
                return IRQ_NONE;
        }