Commit 0da1b995 authored by Francois Romieu's avatar Francois Romieu

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: default avatarFrancois Romieu <romieu@fr.zoreil.com>
parent 227bc24d
...@@ -857,21 +857,14 @@ static void init_tfdlist(struct net_device *dev) ...@@ -857,21 +857,14 @@ static void init_tfdlist(struct net_device *dev)
static void ipg_nic_txfree(struct net_device *dev) static void ipg_nic_txfree(struct net_device *dev)
{ {
struct ipg_nic_private *sp = netdev_priv(dev); struct ipg_nic_private *sp = netdev_priv(dev);
void __iomem *ioaddr = sp->ioaddr; unsigned int released, pending, dirty;
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;
IPG_DEBUG_MSG("_nic_txfree\n"); IPG_DEBUG_MSG("_nic_txfree\n");
pending = sp->tx_current - sp->tx_dirty; pending = sp->tx_current - sp->tx_dirty;
dirty = sp->tx_dirty % IPG_TFDLIST_LENGTH;
for (released = 0; released < pending; released++) { for (released = 0; released < pending; released++) {
unsigned int dirty = sp->tx_dirty % IPG_TFDLIST_LENGTH;
struct sk_buff *skb = sp->TxBuff[dirty]; struct sk_buff *skb = sp->TxBuff[dirty];
struct ipg_tx *txfd = sp->txd + dirty; struct ipg_tx *txfd = sp->txd + dirty;
...@@ -882,12 +875,9 @@ static void ipg_nic_txfree(struct net_device *dev) ...@@ -882,12 +875,9 @@ static void ipg_nic_txfree(struct net_device *dev)
* If the TFDDone bit is set, free the associated * If the TFDDone bit is set, free the associated
* buffer. * buffer.
*/ */
if (dirty == curr) if (!(txfd->tfc & cpu_to_le64(IPG_TFC_TFDDONE)))
break; break;
/* Setup TFDDONE for compatible issue. */
txfd->tfc |= cpu_to_le64(IPG_TFC_TFDDONE);
/* Free the transmit buffer. */ /* Free the transmit buffer. */
if (skb) { if (skb) {
pci_unmap_single(sp->pdev, pci_unmap_single(sp->pdev,
...@@ -898,6 +888,7 @@ static void ipg_nic_txfree(struct net_device *dev) ...@@ -898,6 +888,7 @@ static void ipg_nic_txfree(struct net_device *dev)
sp->TxBuff[dirty] = NULL; sp->TxBuff[dirty] = NULL;
} }
dirty = (dirty + 1) % IPG_TFDLIST_LENGTH;
} }
sp->tx_dirty += released; sp->tx_dirty += released;
......
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