qla2xxx: Prevent removal and board_disable race
Introduce mutual exclusion between the qla2xxx_remove_one PCI driver callback and qla2x00_disable_board_on_pci_error, which is scheduled as board_disable work by qla2x00_check_reg{32,16}_for_disconnect: * Leave the driver-specific data attached to the underlying PCI device intact in qla2x00_disable_board_on_pci_error, so that qla2x00_remove_one has enough breadcrumbs to determine that any board_disable work has been completed. * In qla2xxx_remove_one, set a bit to prevent any subsequent board_disable work from scheduling, then cancel and wait until pending work has completed. * Reuse the PCI device enable count check in qla2x00_remove_one to determine if board_disable has occured. The original purpose of this check was unnecessary since the driver remove function wasn't called when the probe fails. Signed-off-by: Joe Lawrence <joe.lawrence@stratus.com> Acked-by: Chad Dupuis <chad.dupuis@qlogic.com> Signed-off-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
232792b6b4
commit
beb9e315e6
@ -3404,6 +3404,7 @@ typedef struct scsi_qla_host {
|
|||||||
|
|
||||||
unsigned long pci_flags;
|
unsigned long pci_flags;
|
||||||
#define PFLG_DISCONNECTED 0 /* PCI device removed */
|
#define PFLG_DISCONNECTED 0 /* PCI device removed */
|
||||||
|
#define PFLG_DRIVER_REMOVING 1 /* PCI driver .remove */
|
||||||
|
|
||||||
uint32_t device_flags;
|
uint32_t device_flags;
|
||||||
#define SWITCH_FOUND BIT_0
|
#define SWITCH_FOUND BIT_0
|
||||||
|
@ -117,7 +117,8 @@ qla2x00_check_reg32_for_disconnect(scsi_qla_host_t *vha, uint32_t reg)
|
|||||||
{
|
{
|
||||||
/* Check for PCI disconnection */
|
/* Check for PCI disconnection */
|
||||||
if (reg == 0xffffffff) {
|
if (reg == 0xffffffff) {
|
||||||
if (!test_and_set_bit(PFLG_DISCONNECTED, &vha->pci_flags)) {
|
if (!test_and_set_bit(PFLG_DISCONNECTED, &vha->pci_flags) &&
|
||||||
|
!test_bit(PFLG_DRIVER_REMOVING, &vha->pci_flags)) {
|
||||||
/*
|
/*
|
||||||
* Schedule this (only once) on the default system
|
* Schedule this (only once) on the default system
|
||||||
* workqueue so that all the adapter workqueues and the
|
* workqueue so that all the adapter workqueues and the
|
||||||
|
@ -3131,16 +3131,26 @@ qla2x00_remove_one(struct pci_dev *pdev)
|
|||||||
scsi_qla_host_t *base_vha;
|
scsi_qla_host_t *base_vha;
|
||||||
struct qla_hw_data *ha;
|
struct qla_hw_data *ha;
|
||||||
|
|
||||||
/*
|
|
||||||
* If the PCI device is disabled that means that probe failed and any
|
|
||||||
* resources should be have cleaned up on probe exit.
|
|
||||||
*/
|
|
||||||
if (!atomic_read(&pdev->enable_cnt))
|
|
||||||
return;
|
|
||||||
|
|
||||||
base_vha = pci_get_drvdata(pdev);
|
base_vha = pci_get_drvdata(pdev);
|
||||||
ha = base_vha->hw;
|
ha = base_vha->hw;
|
||||||
|
|
||||||
|
/* Indicate device removal to prevent future board_disable and wait
|
||||||
|
* until any pending board_disable has completed. */
|
||||||
|
set_bit(PFLG_DRIVER_REMOVING, &base_vha->pci_flags);
|
||||||
|
cancel_work_sync(&ha->board_disable);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the PCI device is disabled then there was a PCI-disconnect and
|
||||||
|
* qla2x00_disable_board_on_pci_error has taken care of most of the
|
||||||
|
* resources.
|
||||||
|
*/
|
||||||
|
if (!atomic_read(&pdev->enable_cnt)) {
|
||||||
|
scsi_host_put(base_vha->host);
|
||||||
|
kfree(ha);
|
||||||
|
pci_set_drvdata(pdev, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
qla2x00_wait_for_hba_ready(base_vha);
|
qla2x00_wait_for_hba_ready(base_vha);
|
||||||
|
|
||||||
set_bit(UNLOADING, &base_vha->dpc_flags);
|
set_bit(UNLOADING, &base_vha->dpc_flags);
|
||||||
@ -4799,18 +4809,15 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
|
|||||||
qla82xx_md_free(base_vha);
|
qla82xx_md_free(base_vha);
|
||||||
qla2x00_free_queues(ha);
|
qla2x00_free_queues(ha);
|
||||||
|
|
||||||
scsi_host_put(base_vha->host);
|
|
||||||
|
|
||||||
qla2x00_unmap_iobases(ha);
|
qla2x00_unmap_iobases(ha);
|
||||||
|
|
||||||
pci_release_selected_regions(ha->pdev, ha->bars);
|
pci_release_selected_regions(ha->pdev, ha->bars);
|
||||||
kfree(ha);
|
|
||||||
ha = NULL;
|
|
||||||
|
|
||||||
pci_disable_pcie_error_reporting(pdev);
|
pci_disable_pcie_error_reporting(pdev);
|
||||||
pci_disable_device(pdev);
|
pci_disable_device(pdev);
|
||||||
pci_set_drvdata(pdev, NULL);
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Let qla2x00_remove_one cleanup qla_hw_data on device removal.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
|
Loading…
x
Reference in New Issue
Block a user