From 227bc24d675d80de1cfb3ab72891cc932dadbc3b Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Thu, 10 Jan 2008 23:25:30 +0100 Subject: ipg: balance locking in irq handler Spotted-by: Signed-off-by: Francois Romieu --- drivers/net/ipg.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c index dbd23bb65d1e..cd1650e99369 100644 --- a/drivers/net/ipg.c +++ b/drivers/net/ipg.c @@ -1630,6 +1630,8 @@ static irqreturn_t ipg_interrupt_handler(int irq, void *dev_inst) #ifdef JUMBO_FRAME ipg_nic_rxrestore(dev); #endif + spin_lock(&sp->lock); + /* Get interrupt source information, and acknowledge * some (i.e. TxDMAComplete, RxDMAComplete, RxEarly, * IntRequested, MacControlFrame, LinkEvent) interrupts @@ -1647,9 +1649,7 @@ static irqreturn_t ipg_interrupt_handler(int irq, void *dev_inst) handled = 1; if (unlikely(!netif_running(dev))) - goto out; - - spin_lock(&sp->lock); + goto out_unlock; /* If RFDListEnd interrupt, restore all used RFDs. */ if (status & IPG_IS_RFD_LIST_END) { @@ -1733,9 +1733,9 @@ out_enable: ipg_w16(IPG_IE_TX_DMA_COMPLETE | IPG_IE_RX_DMA_COMPLETE | IPG_IE_HOST_ERROR | IPG_IE_INT_REQUESTED | IPG_IE_TX_COMPLETE | IPG_IE_LINK_EVENT | IPG_IE_UPDATE_STATS, INT_ENABLE); - +out_unlock: spin_unlock(&sp->lock); -out: + return IRQ_RETVAL(handled); } -- cgit v1.2.1 From 0da1b995aee447656c0eb77e4e32468e37f868a3 Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Thu, 10 Jan 2008 23:40:59 +0100 Subject: ipg: plug Tx completion leak The Tx skb release could not free more than one skb per call. Add it to the fact that the xmit handler does not check for a queue full condition and you have a recipe to leak quickly. Let's release every pending Tx descriptor which has been given back to the host CPU by the network controller. The xmit handler suggests that it is done through the IPG_TFC_TFDDONE bit. Remove the former "curr" computing: it does not produce anything usable in its current form. Signed-off-by: Francois Romieu --- drivers/net/ipg.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c index cd1650e99369..9752902f2d9a 100644 --- a/drivers/net/ipg.c +++ b/drivers/net/ipg.c @@ -857,21 +857,14 @@ static void init_tfdlist(struct net_device *dev) static void ipg_nic_txfree(struct net_device *dev) { struct ipg_nic_private *sp = netdev_priv(dev); - void __iomem *ioaddr = sp->ioaddr; - unsigned int curr; - u64 txd_map; - unsigned int released, pending; - - txd_map = (u64)sp->txd_map; - curr = ipg_r32(TFD_LIST_PTR_0) - - do_div(txd_map, sizeof(struct ipg_tx)) - 1; + unsigned int released, pending, dirty; IPG_DEBUG_MSG("_nic_txfree\n"); pending = sp->tx_current - sp->tx_dirty; + dirty = sp->tx_dirty % IPG_TFDLIST_LENGTH; for (released = 0; released < pending; released++) { - unsigned int dirty = sp->tx_dirty % IPG_TFDLIST_LENGTH; struct sk_buff *skb = sp->TxBuff[dirty]; struct ipg_tx *txfd = sp->txd + dirty; @@ -882,11 +875,8 @@ static void ipg_nic_txfree(struct net_device *dev) * If the TFDDone bit is set, free the associated * buffer. */ - if (dirty == curr) - break; - - /* Setup TFDDONE for compatible issue. */ - txfd->tfc |= cpu_to_le64(IPG_TFC_TFDDONE); + if (!(txfd->tfc & cpu_to_le64(IPG_TFC_TFDDONE))) + break; /* Free the transmit buffer. */ if (skb) { @@ -898,6 +888,7 @@ static void ipg_nic_txfree(struct net_device *dev) sp->TxBuff[dirty] = NULL; } + dirty = (dirty + 1) % IPG_TFDLIST_LENGTH; } sp->tx_dirty += released; -- cgit v1.2.1 From dafdec746f8c468bebf6b99f32a392ee6c8d0212 Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Thu, 10 Jan 2008 23:45:05 +0100 Subject: ipg: fix queue stop condition in the xmit handler Signed-off-by: Francois Romieu --- drivers/net/ipg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c index 9752902f2d9a..b234b293c11f 100644 --- a/drivers/net/ipg.c +++ b/drivers/net/ipg.c @@ -1994,7 +1994,7 @@ static int ipg_nic_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ipg_w32(IPG_DC_TX_DMA_POLL_NOW, DMA_CTRL); if (sp->tx_current == (sp->tx_dirty + IPG_TFDLIST_LENGTH)) - netif_wake_queue(dev); + netif_stop_queue(dev); spin_unlock_irqrestore(&sp->lock, flags); -- cgit v1.2.1 From 47cccd7d7cc1f2b6f34aadc9041fb991c6293cdd Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Thu, 10 Jan 2008 23:53:15 +0100 Subject: ipg: fix Tx completion irq request The current logic will only request an ack for the first pending packet. No irq is triggered as soon as the CPU submits a few packets a bit quickly. Let's request an irq for every packet instead. Signed-off-by: Francois Romieu --- drivers/net/ipg.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c index b234b293c11f..50f0c17451b1 100644 --- a/drivers/net/ipg.c +++ b/drivers/net/ipg.c @@ -1934,10 +1934,7 @@ static int ipg_nic_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) */ if (sp->tenmbpsmode) txfd->tfc |= cpu_to_le64(IPG_TFC_TXINDICATE); - else if (!((sp->tx_current - sp->tx_dirty + 1) > - IPG_FRAMESBETWEENTXDMACOMPLETES)) { - txfd->tfc |= cpu_to_le64(IPG_TFC_TXDMAINDICATE); - } + txfd->tfc |= cpu_to_le64(IPG_TFC_TXDMAINDICATE); /* Based on compilation option, determine if FCS is to be * appended to transmit frame by IPG. */ -- cgit v1.2.1