diff options
author | Eric Dumazet <edumazet@google.com> | 2012-05-14 05:26:06 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-05-15 13:41:43 -0400 |
commit | 3ab77bf271e6a41512e366dfa5110edb981ed1d3 (patch) | |
tree | d3ff26916d49bba577ea29fc58fe4f3319366056 /drivers | |
parent | 4e6304b8420aba5311ba21fd68dab2924ae4d91a (diff) |
pch_gbe: fix transmit races
Andy reported pch_gbe triggered "NETDEV WATCHDOG" errors.
May 11 11:06:09 kontron kernel: WARNING: at net/sched/sch_generic.c:261
dev_watchdog+0x1ec/0x200() (Not tainted)
May 11 11:06:09 kontron kernel: Hardware name: N/A
May 11 11:06:09 kontron kernel: NETDEV WATCHDOG: eth0 (pch_gbe):
transmit queue 0 timed out
It seems pch_gbe has a racy tx path (races with TX completion path)
Remove tx_queue_lock lock since it has no purpose, we must use tx_lock
instead.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Andy Cress <andy.cress@us.kontron.com>
Tested-by: Andy Cress <andy.cress@us.kontron.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 25 |
2 files changed, 11 insertions, 16 deletions
diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h index dd14915f54bb..ba781747d174 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h | |||
@@ -584,7 +584,6 @@ struct pch_gbe_hw_stats { | |||
584 | /** | 584 | /** |
585 | * struct pch_gbe_adapter - board specific private data structure | 585 | * struct pch_gbe_adapter - board specific private data structure |
586 | * @stats_lock: Spinlock structure for status | 586 | * @stats_lock: Spinlock structure for status |
587 | * @tx_queue_lock: Spinlock structure for transmit | ||
588 | * @ethtool_lock: Spinlock structure for ethtool | 587 | * @ethtool_lock: Spinlock structure for ethtool |
589 | * @irq_sem: Semaphore for interrupt | 588 | * @irq_sem: Semaphore for interrupt |
590 | * @netdev: Pointer of network device structure | 589 | * @netdev: Pointer of network device structure |
@@ -609,7 +608,6 @@ struct pch_gbe_hw_stats { | |||
609 | 608 | ||
610 | struct pch_gbe_adapter { | 609 | struct pch_gbe_adapter { |
611 | spinlock_t stats_lock; | 610 | spinlock_t stats_lock; |
612 | spinlock_t tx_queue_lock; | ||
613 | spinlock_t ethtool_lock; | 611 | spinlock_t ethtool_lock; |
614 | atomic_t irq_sem; | 612 | atomic_t irq_sem; |
615 | struct net_device *netdev; | 613 | struct net_device *netdev; |
diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index 8035e5ff6e06..1e38d502a062 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c | |||
@@ -640,14 +640,11 @@ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw) | |||
640 | */ | 640 | */ |
641 | static int pch_gbe_alloc_queues(struct pch_gbe_adapter *adapter) | 641 | static int pch_gbe_alloc_queues(struct pch_gbe_adapter *adapter) |
642 | { | 642 | { |
643 | int size; | 643 | adapter->tx_ring = kzalloc(sizeof(*adapter->tx_ring), GFP_KERNEL); |
644 | |||
645 | size = (int)sizeof(struct pch_gbe_tx_ring); | ||
646 | adapter->tx_ring = kzalloc(size, GFP_KERNEL); | ||
647 | if (!adapter->tx_ring) | 644 | if (!adapter->tx_ring) |
648 | return -ENOMEM; | 645 | return -ENOMEM; |
649 | size = (int)sizeof(struct pch_gbe_rx_ring); | 646 | |
650 | adapter->rx_ring = kzalloc(size, GFP_KERNEL); | 647 | adapter->rx_ring = kzalloc(sizeof(*adapter->rx_ring), GFP_KERNEL); |
651 | if (!adapter->rx_ring) { | 648 | if (!adapter->rx_ring) { |
652 | kfree(adapter->tx_ring); | 649 | kfree(adapter->tx_ring); |
653 | return -ENOMEM; | 650 | return -ENOMEM; |
@@ -1162,7 +1159,6 @@ static void pch_gbe_tx_queue(struct pch_gbe_adapter *adapter, | |||
1162 | struct sk_buff *tmp_skb; | 1159 | struct sk_buff *tmp_skb; |
1163 | unsigned int frame_ctrl; | 1160 | unsigned int frame_ctrl; |
1164 | unsigned int ring_num; | 1161 | unsigned int ring_num; |
1165 | unsigned long flags; | ||
1166 | 1162 | ||
1167 | /*-- Set frame control --*/ | 1163 | /*-- Set frame control --*/ |
1168 | frame_ctrl = 0; | 1164 | frame_ctrl = 0; |
@@ -1211,14 +1207,14 @@ static void pch_gbe_tx_queue(struct pch_gbe_adapter *adapter, | |||
1211 | } | 1207 | } |
1212 | } | 1208 | } |
1213 | } | 1209 | } |
1214 | spin_lock_irqsave(&tx_ring->tx_lock, flags); | 1210 | |
1215 | ring_num = tx_ring->next_to_use; | 1211 | ring_num = tx_ring->next_to_use; |
1216 | if (unlikely((ring_num + 1) == tx_ring->count)) | 1212 | if (unlikely((ring_num + 1) == tx_ring->count)) |
1217 | tx_ring->next_to_use = 0; | 1213 | tx_ring->next_to_use = 0; |
1218 | else | 1214 | else |
1219 | tx_ring->next_to_use = ring_num + 1; | 1215 | tx_ring->next_to_use = ring_num + 1; |
1220 | 1216 | ||
1221 | spin_unlock_irqrestore(&tx_ring->tx_lock, flags); | 1217 | |
1222 | buffer_info = &tx_ring->buffer_info[ring_num]; | 1218 | buffer_info = &tx_ring->buffer_info[ring_num]; |
1223 | tmp_skb = buffer_info->skb; | 1219 | tmp_skb = buffer_info->skb; |
1224 | 1220 | ||
@@ -1518,7 +1514,7 @@ pch_gbe_alloc_rx_buffers_pool(struct pch_gbe_adapter *adapter, | |||
1518 | &rx_ring->rx_buff_pool_logic, | 1514 | &rx_ring->rx_buff_pool_logic, |
1519 | GFP_KERNEL); | 1515 | GFP_KERNEL); |
1520 | if (!rx_ring->rx_buff_pool) { | 1516 | if (!rx_ring->rx_buff_pool) { |
1521 | pr_err("Unable to allocate memory for the receive poll buffer\n"); | 1517 | pr_err("Unable to allocate memory for the receive pool buffer\n"); |
1522 | return -ENOMEM; | 1518 | return -ENOMEM; |
1523 | } | 1519 | } |
1524 | memset(rx_ring->rx_buff_pool, 0, size); | 1520 | memset(rx_ring->rx_buff_pool, 0, size); |
@@ -1637,15 +1633,17 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, | |||
1637 | pr_debug("called pch_gbe_unmap_and_free_tx_resource() %d count\n", | 1633 | pr_debug("called pch_gbe_unmap_and_free_tx_resource() %d count\n", |
1638 | cleaned_count); | 1634 | cleaned_count); |
1639 | /* Recover from running out of Tx resources in xmit_frame */ | 1635 | /* Recover from running out of Tx resources in xmit_frame */ |
1636 | spin_lock(&tx_ring->tx_lock); | ||
1640 | if (unlikely(cleaned && (netif_queue_stopped(adapter->netdev)))) { | 1637 | if (unlikely(cleaned && (netif_queue_stopped(adapter->netdev)))) { |
1641 | netif_wake_queue(adapter->netdev); | 1638 | netif_wake_queue(adapter->netdev); |
1642 | adapter->stats.tx_restart_count++; | 1639 | adapter->stats.tx_restart_count++; |
1643 | pr_debug("Tx wake queue\n"); | 1640 | pr_debug("Tx wake queue\n"); |
1644 | } | 1641 | } |
1645 | spin_lock(&adapter->tx_queue_lock); | 1642 | |
1646 | tx_ring->next_to_clean = i; | 1643 | tx_ring->next_to_clean = i; |
1647 | spin_unlock(&adapter->tx_queue_lock); | 1644 | |
1648 | pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean); | 1645 | pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean); |
1646 | spin_unlock(&tx_ring->tx_lock); | ||
1649 | return cleaned; | 1647 | return cleaned; |
1650 | } | 1648 | } |
1651 | 1649 | ||
@@ -2037,7 +2035,6 @@ static int pch_gbe_sw_init(struct pch_gbe_adapter *adapter) | |||
2037 | return -ENOMEM; | 2035 | return -ENOMEM; |
2038 | } | 2036 | } |
2039 | spin_lock_init(&adapter->hw.miim_lock); | 2037 | spin_lock_init(&adapter->hw.miim_lock); |
2040 | spin_lock_init(&adapter->tx_queue_lock); | ||
2041 | spin_lock_init(&adapter->stats_lock); | 2038 | spin_lock_init(&adapter->stats_lock); |
2042 | spin_lock_init(&adapter->ethtool_lock); | 2039 | spin_lock_init(&adapter->ethtool_lock); |
2043 | atomic_set(&adapter->irq_sem, 0); | 2040 | atomic_set(&adapter->irq_sem, 0); |
@@ -2142,10 +2139,10 @@ static int pch_gbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) | |||
2142 | tx_ring->next_to_use, tx_ring->next_to_clean); | 2139 | tx_ring->next_to_use, tx_ring->next_to_clean); |
2143 | return NETDEV_TX_BUSY; | 2140 | return NETDEV_TX_BUSY; |
2144 | } | 2141 | } |
2145 | spin_unlock_irqrestore(&tx_ring->tx_lock, flags); | ||
2146 | 2142 | ||
2147 | /* CRC,ITAG no support */ | 2143 | /* CRC,ITAG no support */ |
2148 | pch_gbe_tx_queue(adapter, tx_ring, skb); | 2144 | pch_gbe_tx_queue(adapter, tx_ring, skb); |
2145 | spin_unlock_irqrestore(&tx_ring->tx_lock, flags); | ||
2149 | return NETDEV_TX_OK; | 2146 | return NETDEV_TX_OK; |
2150 | } | 2147 | } |
2151 | 2148 | ||