Commit 302d1252 authored by Stephen Hemminger's avatar Stephen Hemminger Committed by Jeff Garzik

[PATCH] sky2: more conservative transmit locking

Be more careful about transmit locking, this solves a possible race
between tx_complete and transmit, that would cause a tx timeout.
Signed-off-by: default avatarStephen Hemminger <shemminger@osdl.org>
Signed-off-by: default avatarJeff Garzik <jgarzik@pobox.com>
parent 28bd181a
...@@ -889,13 +889,13 @@ static void sky2_vlan_rx_register(struct net_device *dev, struct vlan_group *grp ...@@ -889,13 +889,13 @@ static void sky2_vlan_rx_register(struct net_device *dev, struct vlan_group *grp
struct sky2_hw *hw = sky2->hw; struct sky2_hw *hw = sky2->hw;
u16 port = sky2->port; u16 port = sky2->port;
spin_lock(&sky2->tx_lock); spin_lock_bh(&sky2->tx_lock);
sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_ON); sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_ON);
sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_ON); sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_ON);
sky2->vlgrp = grp; sky2->vlgrp = grp;
spin_unlock(&sky2->tx_lock); spin_unlock_bh(&sky2->tx_lock);
} }
static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
...@@ -904,14 +904,14 @@ static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) ...@@ -904,14 +904,14 @@ static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
struct sky2_hw *hw = sky2->hw; struct sky2_hw *hw = sky2->hw;
u16 port = sky2->port; u16 port = sky2->port;
spin_lock(&sky2->tx_lock); spin_lock_bh(&sky2->tx_lock);
sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_OFF); sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_OFF);
sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_OFF); sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_OFF);
if (sky2->vlgrp) if (sky2->vlgrp)
sky2->vlgrp->vlan_devices[vid] = NULL; sky2->vlgrp->vlan_devices[vid] = NULL;
spin_unlock(&sky2->tx_lock); spin_unlock_bh(&sky2->tx_lock);
} }
#endif #endif
...@@ -1116,6 +1116,10 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) ...@@ -1116,6 +1116,10 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
u16 mss; u16 mss;
u8 ctrl; u8 ctrl;
/* No BH disabling for tx_lock here. We are running in BH disabled
* context and TX reclaim runs via poll inside of a software
* interrupt, and no related locks in IRQ processing.
*/
if (!spin_trylock(&sky2->tx_lock)) if (!spin_trylock(&sky2->tx_lock))
return NETDEV_TX_LOCKED; return NETDEV_TX_LOCKED;
...@@ -1308,17 +1312,17 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) ...@@ -1308,17 +1312,17 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
} }
spin_lock(&sky2->tx_lock);
sky2->tx_cons = put; sky2->tx_cons = put;
if (netif_queue_stopped(dev) && tx_avail(sky2) > MAX_SKB_TX_LE) if (netif_queue_stopped(dev) && tx_avail(sky2) > MAX_SKB_TX_LE)
netif_wake_queue(dev); netif_wake_queue(dev);
spin_unlock(&sky2->tx_lock);
} }
/* Cleanup all untransmitted buffers, assume transmitter not running */ /* Cleanup all untransmitted buffers, assume transmitter not running */
static void sky2_tx_clean(struct sky2_port *sky2) static void sky2_tx_clean(struct sky2_port *sky2)
{ {
spin_lock_bh(&sky2->tx_lock);
sky2_tx_complete(sky2, sky2->tx_prod); sky2_tx_complete(sky2, sky2->tx_prod);
spin_unlock_bh(&sky2->tx_lock);
} }
/* Network shutdown */ /* Network shutdown */
...@@ -1608,28 +1612,40 @@ static void sky2_phy_task(void *arg) ...@@ -1608,28 +1612,40 @@ static void sky2_phy_task(void *arg)
local_irq_enable(); local_irq_enable();
} }
/* Transmit timeout is only called if we are running, carries is up
* and tx queue is full (stopped).
*/
static void sky2_tx_timeout(struct net_device *dev) static void sky2_tx_timeout(struct net_device *dev)
{ {
struct sky2_port *sky2 = netdev_priv(dev); struct sky2_port *sky2 = netdev_priv(dev);
struct sky2_hw *hw = sky2->hw; struct sky2_hw *hw = sky2->hw;
unsigned txq = txqaddr[sky2->port]; unsigned txq = txqaddr[sky2->port];
u16 ridx;
/* Maybe we just missed an status interrupt */
spin_lock(&sky2->tx_lock);
ridx = sky2_read16(hw,
sky2->port == 0 ? STAT_TXA1_RIDX : STAT_TXA2_RIDX);
sky2_tx_complete(sky2, ridx);
spin_unlock(&sky2->tx_lock);
if (!netif_queue_stopped(dev)) {
if (net_ratelimit())
pr_info(PFX "transmit interrupt missed? recovered\n");
return;
}
if (netif_msg_timer(sky2)) if (netif_msg_timer(sky2))
printk(KERN_ERR PFX "%s: tx timeout\n", dev->name); printk(KERN_ERR PFX "%s: tx timeout\n", dev->name);
netif_stop_queue(dev);
sky2_write32(hw, Q_ADDR(txq, Q_CSR), BMU_STOP); sky2_write32(hw, Q_ADDR(txq, Q_CSR), BMU_STOP);
sky2_read32(hw, Q_ADDR(txq, Q_CSR));
sky2_write32(hw, Y2_QADDR(txq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET); sky2_write32(hw, Y2_QADDR(txq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
sky2_tx_clean(sky2); sky2_tx_clean(sky2);
sky2_qset(hw, txq); sky2_qset(hw, txq);
sky2_prefetch_init(hw, txq, sky2->tx_le_map, TX_RING_SIZE - 1); sky2_prefetch_init(hw, txq, sky2->tx_le_map, TX_RING_SIZE - 1);
netif_wake_queue(dev);
} }
...@@ -1798,7 +1814,10 @@ static void sky2_tx_check(struct sky2_hw *hw, int port, u16 last) ...@@ -1798,7 +1814,10 @@ static void sky2_tx_check(struct sky2_hw *hw, int port, u16 last)
struct net_device *dev = hw->dev[port]; struct net_device *dev = hw->dev[port];
if (dev && netif_running(dev)) { if (dev && netif_running(dev)) {
struct sky2_port *sky2 = netdev_priv(dev); struct sky2_port *sky2 = netdev_priv(dev);
spin_lock(&sky2->tx_lock);
sky2_tx_complete(sky2, last); sky2_tx_complete(sky2, last);
spin_unlock(&sky2->tx_lock);
} }
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment