PCI / tg3: Give up chip reset and carrier loss handling if PCI device is not present
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Sun, 1 Dec 2013 01:34:37 +0000 (02:34 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 2 Dec 2013 21:01:49 +0000 (16:01 -0500)
Modify tg3_chip_reset() and tg3_close() to check if the PCI network
adapter device is accessible at all in order to skip poking it or
trying to handle a carrier loss in vain when that's not the case.
Introduce a special PCI helper function pci_device_is_present()
for this purpose.

Of course, this uncovers the lack of the appropriate RTNL locking
in tg3_suspend() and tg3_resume(), so add that locking in there
too.

These changes prevent tg3 from burning a CPU at 100% load level for
solid several seconds after the Thunderbolt link is disconnected from
a Matrox DS1 docking station.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/tg3.c
drivers/pci/pci.c
include/linux/pci.h

index 369b736dde0533740a01718fd2d271fa7f10ad60..472305cdff4f576520357e2210cc462e1dac7896 100644 (file)
@@ -8932,6 +8932,9 @@ static int tg3_chip_reset(struct tg3 *tp)
        void (*write_op)(struct tg3 *, u32, u32);
        int i, err;
 
+       if (!pci_device_is_present(tp->pdev))
+               return -ENODEV;
+
        tg3_nvram_lock(tp);
 
        tg3_ape_lock(tp, TG3_APE_LOCK_GRC);
@@ -11581,10 +11584,11 @@ static int tg3_close(struct net_device *dev)
        memset(&tp->net_stats_prev, 0, sizeof(tp->net_stats_prev));
        memset(&tp->estats_prev, 0, sizeof(tp->estats_prev));
 
-       tg3_power_down_prepare(tp);
-
-       tg3_carrier_off(tp);
+       if (pci_device_is_present(tp->pdev)) {
+               tg3_power_down_prepare(tp);
 
+               tg3_carrier_off(tp);
+       }
        return 0;
 }
 
@@ -17726,10 +17730,12 @@ static int tg3_suspend(struct device *device)
        struct pci_dev *pdev = to_pci_dev(device);
        struct net_device *dev = pci_get_drvdata(pdev);
        struct tg3 *tp = netdev_priv(dev);
-       int err;
+       int err = 0;
+
+       rtnl_lock();
 
        if (!netif_running(dev))
-               return 0;
+               goto unlock;
 
        tg3_reset_task_cancel(tp);
        tg3_phy_stop(tp);
@@ -17771,6 +17777,8 @@ out:
                        tg3_phy_start(tp);
        }
 
+unlock:
+       rtnl_unlock();
        return err;
 }
 
@@ -17779,10 +17787,12 @@ static int tg3_resume(struct device *device)
        struct pci_dev *pdev = to_pci_dev(device);
        struct net_device *dev = pci_get_drvdata(pdev);
        struct tg3 *tp = netdev_priv(dev);
-       int err;
+       int err = 0;
+
+       rtnl_lock();
 
        if (!netif_running(dev))
-               return 0;
+               goto unlock;
 
        netif_device_attach(dev);
 
@@ -17806,6 +17816,8 @@ out:
        if (!err)
                tg3_phy_start(tp);
 
+unlock:
+       rtnl_unlock();
        return err;
 }
 #endif /* CONFIG_PM_SLEEP */
index 33120d15666895a2b48aec1c83f7b41f7b93c3be..07369f32e8bbdd79b1294ad844baeb7dc80ef7ea 100644 (file)
@@ -4165,6 +4165,14 @@ int pci_set_vga_state(struct pci_dev *dev, bool decode,
        return 0;
 }
 
+bool pci_device_is_present(struct pci_dev *pdev)
+{
+       u32 v;
+
+       return pci_bus_read_dev_vendor_id(pdev->bus, pdev->devfn, &v, 0);
+}
+EXPORT_SYMBOL_GPL(pci_device_is_present);
+
 #define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE
 static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0};
 static DEFINE_SPINLOCK(resource_alignment_lock);
index 1084a15175e04ff0bc715e3e429aab7027d279f8..25a389566e4781ea7c4e4dd1a5aa7422f81d3294 100644 (file)
@@ -960,6 +960,7 @@ void pci_update_resource(struct pci_dev *dev, int resno);
 int __must_check pci_assign_resource(struct pci_dev *dev, int i);
 int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
 int pci_select_bars(struct pci_dev *dev, unsigned long flags);
+bool pci_device_is_present(struct pci_dev *pdev);
 
 /* ROM control related routines */
 int pci_enable_rom(struct pci_dev *pdev);