tg3: prevent ifup/ifdown during PCI error recovery
[pandora-kernel.git] / drivers / net / ethernet / broadcom / tg3.c
index 8afa579..cb77ae9 100644 (file)
@@ -237,7 +237,7 @@ MODULE_PARM_DESC(tg3_debug, "Tigon3 bitmapped debugging message enable value");
 #define TG3_DRV_DATA_FLAG_10_100_ONLY  0x0001
 #define TG3_DRV_DATA_FLAG_5705_10_100  0x0002
 
-static DEFINE_PCI_DEVICE_TABLE(tg3_pci_tbl) = {
+static const struct pci_device_id tg3_pci_tbl[] = {
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5700)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5701)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702)},
@@ -7830,17 +7830,18 @@ static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi,
 
 static netdev_tx_t tg3_start_xmit(struct sk_buff *, struct net_device *);
 
-/* Use GSO to workaround a rare TSO bug that may be triggered when the
- * TSO header is greater than 80 bytes.
+/* Use GSO to workaround all TSO packets that meet HW bug conditions
+ * indicated in tg3_tx_frag_set()
  */
-static int tg3_tso_bug(struct tg3 *tp, struct sk_buff *skb)
+static int tg3_tso_bug(struct tg3 *tp, struct tg3_napi *tnapi,
+                      struct netdev_queue *txq, struct sk_buff *skb)
 {
        struct sk_buff *segs, *nskb;
        u32 frag_cnt_est = skb_shinfo(skb)->gso_segs * 3;
 
        /* Estimate the number of fragments in the worst case */
-       if (unlikely(tg3_tx_avail(&tp->napi[0]) <= frag_cnt_est)) {
-               netif_stop_queue(tp->dev);
+       if (unlikely(tg3_tx_avail(tnapi) <= frag_cnt_est)) {
+               netif_tx_stop_queue(txq);
 
                /* netif_tx_stop_queue() must be done before checking
                 * checking tx index in tg3_tx_avail() below, because in
@@ -7848,13 +7849,14 @@ static int tg3_tso_bug(struct tg3 *tp, struct sk_buff *skb)
                 * netif_tx_queue_stopped().
                 */
                smp_mb();
-               if (tg3_tx_avail(&tp->napi[0]) <= frag_cnt_est)
+               if (tg3_tx_avail(tnapi) <= frag_cnt_est)
                        return NETDEV_TX_BUSY;
 
-               netif_wake_queue(tp->dev);
+               netif_tx_wake_queue(txq);
        }
 
-       segs = skb_gso_segment(skb, tp->dev->features & ~(NETIF_F_TSO | NETIF_F_TSO6));
+       segs = skb_gso_segment(skb, tp->dev->features &
+                                   ~(NETIF_F_TSO | NETIF_F_TSO6));
        if (IS_ERR(segs) || !segs)
                goto tg3_tso_bug_end;
 
@@ -7930,7 +7932,7 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
                if (!skb_is_gso_v6(skb)) {
                        if (unlikely((ETH_HLEN + hdr_len) > 80) &&
                            tg3_flag(tp, TSO_BUG))
-                               return tg3_tso_bug(tp, skb);
+                               return tg3_tso_bug(tp, tnapi, txq, skb);
 
                        ip_csum = iph->check;
                        ip_tot_len = iph->tot_len;
@@ -8061,7 +8063,7 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
                                iph->tot_len = ip_tot_len;
                        }
                        tcph->check = tcp_csum;
-                       return tg3_tso_bug(tp, skb);
+                       return tg3_tso_bug(tp, tnapi, txq, skb);
                }
 
                /* If the workaround fails due to memory/mapping
@@ -11615,6 +11617,12 @@ static int tg3_open(struct net_device *dev)
        struct tg3 *tp = netdev_priv(dev);
        int err;
 
+       if (tp->pcierr_recovery) {
+               netdev_err(dev, "Failed to open device. PCI error recovery "
+                          "in progress\n");
+               return -EAGAIN;
+       }
+
        if (tp->fw_needed) {
                err = tg3_request_firmware(tp);
                if (tg3_asic_rev(tp) == ASIC_REV_57766) {
@@ -11672,6 +11680,12 @@ static int tg3_close(struct net_device *dev)
 {
        struct tg3 *tp = netdev_priv(dev);
 
+       if (tp->pcierr_recovery) {
+               netdev_err(dev, "Failed to close device. PCI error recovery "
+                          "in progress\n");
+               return -EAGAIN;
+       }
+
        tg3_ptp_fini(tp);
 
        tg3_stop(tp);
@@ -14091,8 +14105,9 @@ static struct rtnl_link_stats64 *tg3_get_stats64(struct net_device *dev,
 
        spin_lock_bh(&tp->lock);
        if (!tp->hw_stats) {
+               *stats = tp->net_stats_prev;
                spin_unlock_bh(&tp->lock);
-               return &tp->net_stats_prev;
+               return stats;
        }
 
        tg3_get_nstats(tp, stats);
@@ -15924,7 +15939,7 @@ static inline u32 tg3_rx_ret_ring_size(struct tg3 *tp)
                return TG3_RX_RET_MAX_SIZE_5705;
 }
 
-static DEFINE_PCI_DEVICE_TABLE(tg3_write_reorder_chipsets) = {
+static const struct pci_device_id tg3_write_reorder_chipsets[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C) },
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE) },
        { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8385_0) },
@@ -17183,7 +17198,7 @@ static int tg3_do_test_dma(struct tg3 *tp, u32 *buf, dma_addr_t buf_dma,
 
 #define TEST_BUFFER_SIZE       0x2000
 
-static DEFINE_PCI_DEVICE_TABLE(tg3_dma_wait_state_chipsets) = {
+static const struct pci_device_id tg3_dma_wait_state_chipsets[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_PCI15) },
        { },
 };
@@ -17558,6 +17573,7 @@ static int tg3_init_one(struct pci_dev *pdev,
        tp->rx_mode = TG3_DEF_RX_MODE;
        tp->tx_mode = TG3_DEF_TX_MODE;
        tp->irq_sync = 1;
+       tp->pcierr_recovery = false;
 
        if (tg3_debug > 0)
                tp->msg_enable = tg3_debug;
@@ -18068,6 +18084,8 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev,
 
        rtnl_lock();
 
+       tp->pcierr_recovery = true;
+
        /* We probably don't have netdev yet */
        if (!netdev || !netif_running(netdev))
                goto done;
@@ -18192,6 +18210,7 @@ static void tg3_io_resume(struct pci_dev *pdev)
        tg3_phy_start(tp);
 
 done:
+       tp->pcierr_recovery = false;
        rtnl_unlock();
 }