From 7ce9e1913d4ae45a907e4eda1891893f0adb4176 Mon Sep 17 00:00:00 2001 From: Scott Feldman Date: Tue, 3 Feb 2004 17:05:56 -0500 Subject: [PATCH] [netdrvr e1000] tx_lock * Fix race in Tx performance path with tx_lock. Between checking if we're out of resources and stopping the queue, we can get a hard interrupt which will clean up all Tx work, and wake the queue. Coming out of hard interrupt context, we stop the queue even though no work was queued, and all work completed has been cleaned up. Scenario requires ring to be completely filled, which is more likely to happen with TSO, since each TSO send consumes multiple ring entries. --- drivers/net/e1000/e1000.h | 1 + drivers/net/e1000/e1000_main.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index 99f9a3e5d1931..e3525feb993f4 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -202,6 +202,7 @@ struct e1000_adapter { /* TX */ struct e1000_desc_ring tx_ring; + spinlock_t tx_lock; uint32_t txd_cmd; uint32_t tx_int_delay; uint32_t tx_abs_int_delay; diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 2218f5bfc02ef..c0b1d30d0ded9 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -678,6 +678,7 @@ e1000_sw_init(struct e1000_adapter *adapter) atomic_set(&adapter->irq_sem, 1); spin_lock_init(&adapter->stats_lock); + spin_lock_init(&adapter->tx_lock); return 0; } @@ -1765,6 +1766,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) struct e1000_adapter *adapter = netdev->priv; unsigned int first; unsigned int tx_flags = 0; + unsigned long flags; int count; if(skb->len <= 0) { @@ -1772,10 +1774,13 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) return 0; } + spin_lock_irqsave(&adapter->tx_lock, flags); + if(adapter->hw.mac_type == e1000_82547) { if(e1000_82547_fifo_workaround(adapter, skb)) { netif_stop_queue(netdev); mod_timer(&adapter->tx_fifo_stall_timer, jiffies); + spin_unlock_irqrestore(&adapter->tx_lock, flags); return 1; } } @@ -1796,11 +1801,14 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) e1000_tx_queue(adapter, count, tx_flags); else { netif_stop_queue(netdev); + spin_unlock_irqrestore(&adapter->tx_lock, flags); return 1; } netdev->trans_start = jiffies; + spin_unlock_irqrestore(&adapter->tx_lock, flags); + return 0; } @@ -2154,6 +2162,8 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter) unsigned int i, eop; boolean_t cleaned = FALSE; + spin_lock(&adapter->tx_lock); + i = tx_ring->next_to_clean; eop = tx_ring->buffer_info[i].next_to_watch; eop_desc = E1000_TX_DESC(*tx_ring, eop); @@ -2198,6 +2208,8 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter) if(cleaned && netif_queue_stopped(netdev) && netif_carrier_ok(netdev)) netif_wake_queue(netdev); + spin_unlock(&adapter->tx_lock); + return cleaned; } -- 2.53.0