After a PCIe Uncorrectable Error has been reported by an i40e adapter and has been recovered through a Secondary Bus Reset, its driver calls pci_enable_device() without having called pci_disable_device(). This leads to an imbalance of the enable_cnt tracked by the PCI core: Every time error recovery occurs, the enable_cnt keeps growing. If it occurs at least once and the driver is then unbound, the device isn't disabled since the enable_cnt hasn't reached zero (and never again will). The call to pci_enable_device() has almost no effect because the enable_cnt was already incremented in i40e_probe() through the call to pci_enable_device_mem(). The subsequent pci_enable_device() thus bails out after invoking pci_update_current_state(). Remove pci_enable_device(). In lieu of pci_update_current_state(), set the power state to D0 because that's the power state after a Secondary Bus Reset (PCIe r7.0 sec 5.3.1.1). The intended purpose of pci_enable_device() may have been to set the Memory Space Enable bit in the Command register again after reset, but that is already achieved by the subsequent call to pci_restore_state(). Fixes: 41c445ff0f48 ("i40e: main driver core") Signed-off-by: Lukas Wunner Cc: stable@vger.kernel.org # v3.12+ --- drivers/net/ethernet/intel/i40e/i40e_main.c | 29 +++++++-------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 9d6d892602fa..7e87234fde67 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -16439,29 +16439,20 @@ static pci_ers_result_t i40e_pci_error_detected(struct pci_dev *pdev, static pci_ers_result_t i40e_pci_error_slot_reset(struct pci_dev *pdev) { struct i40e_pf *pf = pci_get_drvdata(pdev); - pci_ers_result_t result; u32 reg; dev_dbg(&pdev->dev, "%s\n", __func__); - /* enable I/O and memory of the device */ - if (pci_enable_device(pdev)) { - dev_info(&pdev->dev, - "Cannot re-enable PCI device after reset.\n"); - result = PCI_ERS_RESULT_DISCONNECT; - } else { - pci_set_master(pdev); - pci_restore_state(pdev); - pci_save_state(pdev); - pci_wake_from_d3(pdev, false); - - reg = rd32(&pf->hw, I40E_GLGEN_RTRIG); - if (reg == 0) - result = PCI_ERS_RESULT_RECOVERED; - else - result = PCI_ERS_RESULT_DISCONNECT; - } + pdev->current_state = PCI_D0; + pci_set_master(pdev); + pci_restore_state(pdev); + pci_save_state(pdev); + pci_wake_from_d3(pdev, false); - return result; + reg = rd32(&pf->hw, I40E_GLGEN_RTRIG); + if (reg == 0) + return PCI_ERS_RESULT_RECOVERED; + else + return PCI_ERS_RESULT_DISCONNECT; } /** -- 2.47.2