Commit 523128e5 authored by James Smart's avatar James Smart Committed by Martin K. Petersen
Browse files

scsi: lpfc: Correct irq handling via locks when taking adapter offline



When taking the board offline while performing i/o, unsafe locking errors
occurred and irq level isn't properly managed.

In lpfc_sli_hba_down, spin_lock_irqsave(&phba->hbalock, flags) does not
disable softirqs raised from timer expiry.  It is possible that a softirq is
raised from the lpfc_els_retry_delay routine and recursively requests the same
phba->hbalock spinlock causing deadlock.

Address the deadlocks by creating a new port_list lock. The softirq behavior
can then be managed a level deeper into the calling sequences.

Signed-off-by: default avatarDick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: default avatarJames Smart <james.smart@broadcom.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 0ef01a2d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -964,6 +964,7 @@ struct lpfc_hba {
	uint32_t intr_mode;
#define LPFC_INTR_ERROR	0xFFFFFFFF
	struct list_head port_list;
	spinlock_t port_list_lock;	/* lock for port_list mutations */
	struct lpfc_vport *pport;	/* physical lpfc_vport pointer */
	uint16_t max_vpi;		/* Maximum virtual nports */
#define LPFC_MAX_VPI 0xFFFF		/* Max number of VPI supported */
+3 −3
Original line number Diff line number Diff line
@@ -445,14 +445,14 @@ lpfc_find_vport_by_did(struct lpfc_hba *phba, uint32_t did) {
	struct lpfc_vport *vport_curr;
	unsigned long flags;

	spin_lock_irqsave(&phba->hbalock, flags);
	spin_lock_irqsave(&phba->port_list_lock, flags);
	list_for_each_entry(vport_curr, &phba->port_list, listentry) {
		if ((vport_curr->fc_myDID) && (vport_curr->fc_myDID == did)) {
			spin_unlock_irqrestore(&phba->hbalock, flags);
			spin_unlock_irqrestore(&phba->port_list_lock, flags);
			return vport_curr;
		}
	}
	spin_unlock_irqrestore(&phba->hbalock, flags);
	spin_unlock_irqrestore(&phba->port_list_lock, flags);
	return NULL;
}

+3 −0
Original line number Diff line number Diff line
@@ -7673,8 +7673,11 @@ void
lpfc_els_flush_all_cmd(struct lpfc_hba  *phba)
{
	struct lpfc_vport *vport;

	spin_lock_irq(&phba->port_list_lock);
	list_for_each_entry(vport, &phba->port_list, listentry)
		lpfc_els_flush_cmd(vport);
	spin_unlock_irq(&phba->port_list_lock);

	return;
}
+3 −3
Original line number Diff line number Diff line
@@ -5938,14 +5938,14 @@ lpfc_find_vport_by_vpid(struct lpfc_hba *phba, uint16_t vpi)
		}
	}

	spin_lock_irqsave(&phba->hbalock, flags);
	spin_lock_irqsave(&phba->port_list_lock, flags);
	list_for_each_entry(vport, &phba->port_list, listentry) {
		if (vport->vpi == i) {
			spin_unlock_irqrestore(&phba->hbalock, flags);
			spin_unlock_irqrestore(&phba->port_list_lock, flags);
			return vport;
		}
	}
	spin_unlock_irqrestore(&phba->hbalock, flags);
	spin_unlock_irqrestore(&phba->port_list_lock, flags);
	return NULL;
}

+11 −8
Original line number Diff line number Diff line
@@ -3988,9 +3988,9 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
	if (error)
		goto out_put_shost;

	spin_lock_irq(&phba->hbalock);
	spin_lock_irq(&phba->port_list_lock);
	list_add_tail(&vport->listentry, &phba->port_list);
	spin_unlock_irq(&phba->hbalock);
	spin_unlock_irq(&phba->port_list_lock);
	return vport;

out_put_shost:
@@ -4016,9 +4016,9 @@ destroy_port(struct lpfc_vport *vport)
	fc_remove_host(shost);
	scsi_remove_host(shost);

	spin_lock_irq(&phba->hbalock);
	spin_lock_irq(&phba->port_list_lock);
	list_del_init(&vport->listentry);
	spin_unlock_irq(&phba->hbalock);
	spin_unlock_irq(&phba->port_list_lock);

	lpfc_cleanup(vport);
	return;
@@ -5621,7 +5621,10 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba)
	/* Initialize ndlp management spinlock */
	spin_lock_init(&phba->ndlp_lock);

	/* Initialize port_list spinlock */
	spin_lock_init(&phba->port_list_lock);
	INIT_LIST_HEAD(&phba->port_list);

	INIT_LIST_HEAD(&phba->work_list);
	init_waitqueue_head(&phba->wait_4_mlo_m_q);

@@ -10985,9 +10988,9 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev)
	kfree(phba->vpi_ids);

	lpfc_stop_hba_timers(phba);
	spin_lock_irq(&phba->hbalock);
	spin_lock_irq(&phba->port_list_lock);
	list_del_init(&vport->listentry);
	spin_unlock_irq(&phba->hbalock);
	spin_unlock_irq(&phba->port_list_lock);

	lpfc_debugfs_terminate(vport);

@@ -11797,9 +11800,9 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev)
	lpfc_sli4_hba_unset(phba);

	lpfc_stop_hba_timers(phba);
	spin_lock_irq(&phba->hbalock);
	spin_lock_irq(&phba->port_list_lock);
	list_del_init(&vport->listentry);
	spin_unlock_irq(&phba->hbalock);
	spin_unlock_irq(&phba->port_list_lock);

	/* Perform scsi free before driver resource_unset since scsi
	 * buffers are released to their corresponding pools here.
Loading