|
@@ -4101,6 +4101,7 @@ static int __devinit ql_init_device(struct pci_dev *pdev,
|
|
goto err_out;
|
|
goto err_out;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ pci_save_state(pdev);
|
|
qdev->reg_base =
|
|
qdev->reg_base =
|
|
ioremap_nocache(pci_resource_start(pdev, 1),
|
|
ioremap_nocache(pci_resource_start(pdev, 1),
|
|
pci_resource_len(pdev, 1));
|
|
pci_resource_len(pdev, 1));
|
|
@@ -4255,6 +4256,33 @@ static void __devexit qlge_remove(struct pci_dev *pdev)
|
|
free_netdev(ndev);
|
|
free_netdev(ndev);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* Clean up resources without touching hardware. */
|
|
|
|
+static void ql_eeh_close(struct net_device *ndev)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+ struct ql_adapter *qdev = netdev_priv(ndev);
|
|
|
|
+
|
|
|
|
+ if (netif_carrier_ok(ndev)) {
|
|
|
|
+ netif_carrier_off(ndev);
|
|
|
|
+ netif_stop_queue(ndev);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (test_bit(QL_ADAPTER_UP, &qdev->flags))
|
|
|
|
+ cancel_delayed_work_sync(&qdev->asic_reset_work);
|
|
|
|
+ cancel_delayed_work_sync(&qdev->mpi_reset_work);
|
|
|
|
+ cancel_delayed_work_sync(&qdev->mpi_work);
|
|
|
|
+ cancel_delayed_work_sync(&qdev->mpi_idc_work);
|
|
|
|
+ cancel_delayed_work_sync(&qdev->mpi_port_cfg_work);
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < qdev->rss_ring_count; i++)
|
|
|
|
+ netif_napi_del(&qdev->rx_ring[i].napi);
|
|
|
|
+
|
|
|
|
+ clear_bit(QL_ADAPTER_UP, &qdev->flags);
|
|
|
|
+ ql_tx_ring_clean(qdev);
|
|
|
|
+ ql_free_rx_buffers(qdev);
|
|
|
|
+ ql_release_adapter_resources(qdev);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* This callback is called by the PCI subsystem whenever
|
|
* This callback is called by the PCI subsystem whenever
|
|
* a PCI bus error is detected.
|
|
* a PCI bus error is detected.
|
|
@@ -4263,17 +4291,21 @@ static pci_ers_result_t qlge_io_error_detected(struct pci_dev *pdev,
|
|
enum pci_channel_state state)
|
|
enum pci_channel_state state)
|
|
{
|
|
{
|
|
struct net_device *ndev = pci_get_drvdata(pdev);
|
|
struct net_device *ndev = pci_get_drvdata(pdev);
|
|
- struct ql_adapter *qdev = netdev_priv(ndev);
|
|
|
|
-
|
|
|
|
- netif_device_detach(ndev);
|
|
|
|
|
|
|
|
- if (state == pci_channel_io_perm_failure)
|
|
|
|
|
|
+ switch (state) {
|
|
|
|
+ case pci_channel_io_normal:
|
|
|
|
+ return PCI_ERS_RESULT_CAN_RECOVER;
|
|
|
|
+ case pci_channel_io_frozen:
|
|
|
|
+ netif_device_detach(ndev);
|
|
|
|
+ if (netif_running(ndev))
|
|
|
|
+ ql_eeh_close(ndev);
|
|
|
|
+ pci_disable_device(pdev);
|
|
|
|
+ return PCI_ERS_RESULT_NEED_RESET;
|
|
|
|
+ case pci_channel_io_perm_failure:
|
|
|
|
+ dev_err(&pdev->dev,
|
|
|
|
+ "%s: pci_channel_io_perm_failure.\n", __func__);
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
-
|
|
|
|
- if (netif_running(ndev))
|
|
|
|
- ql_adapter_down(qdev);
|
|
|
|
-
|
|
|
|
- pci_disable_device(pdev);
|
|
|
|
|
|
+ }
|
|
|
|
|
|
/* Request a slot reset. */
|
|
/* Request a slot reset. */
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
return PCI_ERS_RESULT_NEED_RESET;
|
|
@@ -4290,25 +4322,15 @@ static pci_ers_result_t qlge_io_slot_reset(struct pci_dev *pdev)
|
|
struct net_device *ndev = pci_get_drvdata(pdev);
|
|
struct net_device *ndev = pci_get_drvdata(pdev);
|
|
struct ql_adapter *qdev = netdev_priv(ndev);
|
|
struct ql_adapter *qdev = netdev_priv(ndev);
|
|
|
|
|
|
|
|
+ pdev->error_state = pci_channel_io_normal;
|
|
|
|
+
|
|
|
|
+ pci_restore_state(pdev);
|
|
if (pci_enable_device(pdev)) {
|
|
if (pci_enable_device(pdev)) {
|
|
QPRINTK(qdev, IFUP, ERR,
|
|
QPRINTK(qdev, IFUP, ERR,
|
|
"Cannot re-enable PCI device after reset.\n");
|
|
"Cannot re-enable PCI device after reset.\n");
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
return PCI_ERS_RESULT_DISCONNECT;
|
|
}
|
|
}
|
|
-
|
|
|
|
pci_set_master(pdev);
|
|
pci_set_master(pdev);
|
|
-
|
|
|
|
- netif_carrier_off(ndev);
|
|
|
|
- ql_adapter_reset(qdev);
|
|
|
|
-
|
|
|
|
- /* Make sure the EEPROM is good */
|
|
|
|
- memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len);
|
|
|
|
-
|
|
|
|
- if (!is_valid_ether_addr(ndev->perm_addr)) {
|
|
|
|
- QPRINTK(qdev, IFUP, ERR, "After reset, invalid MAC address.\n");
|
|
|
|
- return PCI_ERS_RESULT_DISCONNECT;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
return PCI_ERS_RESULT_RECOVERED;
|
|
return PCI_ERS_RESULT_RECOVERED;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -4316,17 +4338,21 @@ static void qlge_io_resume(struct pci_dev *pdev)
|
|
{
|
|
{
|
|
struct net_device *ndev = pci_get_drvdata(pdev);
|
|
struct net_device *ndev = pci_get_drvdata(pdev);
|
|
struct ql_adapter *qdev = netdev_priv(ndev);
|
|
struct ql_adapter *qdev = netdev_priv(ndev);
|
|
|
|
+ int err = 0;
|
|
|
|
|
|
- pci_set_master(pdev);
|
|
|
|
-
|
|
|
|
|
|
+ if (ql_adapter_reset(qdev))
|
|
|
|
+ QPRINTK(qdev, DRV, ERR, "reset FAILED!\n");
|
|
if (netif_running(ndev)) {
|
|
if (netif_running(ndev)) {
|
|
- if (ql_adapter_up(qdev)) {
|
|
|
|
|
|
+ err = qlge_open(ndev);
|
|
|
|
+ if (err) {
|
|
QPRINTK(qdev, IFUP, ERR,
|
|
QPRINTK(qdev, IFUP, ERR,
|
|
"Device initialization failed after reset.\n");
|
|
"Device initialization failed after reset.\n");
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
+ } else {
|
|
|
|
+ QPRINTK(qdev, IFUP, ERR,
|
|
|
|
+ "Device was not running prior to EEH.\n");
|
|
}
|
|
}
|
|
-
|
|
|
|
netif_device_attach(ndev);
|
|
netif_device_attach(ndev);
|
|
}
|
|
}
|
|
|
|
|