scsi: lpfc: Fix host hang at boot or slow boot

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>
This commit is contained in:
James Smart 2019-09-21 20:58:58 -07:00 committed by Martin K. Petersen
parent 43bfea1bff
commit 24c7c0a6d3
1 changed files with 36 additions and 6 deletions

View 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;
}