mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-01 06:33:07 +00:00
net: arc_emac: fix sk_buff leak
EMAC could be disabled, while there is some sb_buff in use. That buffers got lost for linux. In order to reproduce run on device during active ethernet work: ifconfig eth0 down Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
99f93a156a
commit
b530b16413
1 changed files with 62 additions and 0 deletions
|
@ -517,6 +517,64 @@ static void arc_emac_set_rx_mode(struct net_device *ndev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* arc_free_tx_queue - free skb from tx queue
|
||||||
|
* @ndev: Pointer to the network device.
|
||||||
|
*
|
||||||
|
* This function must be called while EMAC disable
|
||||||
|
*/
|
||||||
|
static void arc_free_tx_queue(struct net_device *ndev)
|
||||||
|
{
|
||||||
|
struct arc_emac_priv *priv = netdev_priv(ndev);
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < TX_BD_NUM; i++) {
|
||||||
|
struct arc_emac_bd *txbd = &priv->txbd[i];
|
||||||
|
struct buffer_state *tx_buff = &priv->tx_buff[i];
|
||||||
|
|
||||||
|
if (tx_buff->skb) {
|
||||||
|
dma_unmap_single(&ndev->dev, dma_unmap_addr(tx_buff, addr),
|
||||||
|
dma_unmap_len(tx_buff, len), DMA_TO_DEVICE);
|
||||||
|
|
||||||
|
/* return the sk_buff to system */
|
||||||
|
dev_kfree_skb_irq(tx_buff->skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
txbd->info = 0;
|
||||||
|
txbd->data = 0;
|
||||||
|
tx_buff->skb = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* arc_free_rx_queue - free skb from rx queue
|
||||||
|
* @ndev: Pointer to the network device.
|
||||||
|
*
|
||||||
|
* This function must be called while EMAC disable
|
||||||
|
*/
|
||||||
|
static void arc_free_rx_queue(struct net_device *ndev)
|
||||||
|
{
|
||||||
|
struct arc_emac_priv *priv = netdev_priv(ndev);
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < RX_BD_NUM; i++) {
|
||||||
|
struct arc_emac_bd *rxbd = &priv->rxbd[i];
|
||||||
|
struct buffer_state *rx_buff = &priv->rx_buff[i];
|
||||||
|
|
||||||
|
if (rx_buff->skb) {
|
||||||
|
dma_unmap_single(&ndev->dev, dma_unmap_addr(rx_buff, addr),
|
||||||
|
dma_unmap_len(rx_buff, len), DMA_FROM_DEVICE);
|
||||||
|
|
||||||
|
/* return the sk_buff to system */
|
||||||
|
dev_kfree_skb_irq(rx_buff->skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
rxbd->info = 0;
|
||||||
|
rxbd->data = 0;
|
||||||
|
rx_buff->skb = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* arc_emac_stop - Close the network device.
|
* arc_emac_stop - Close the network device.
|
||||||
* @ndev: Pointer to the network device.
|
* @ndev: Pointer to the network device.
|
||||||
|
@ -538,6 +596,10 @@ static int arc_emac_stop(struct net_device *ndev)
|
||||||
/* Disable EMAC */
|
/* Disable EMAC */
|
||||||
arc_reg_clr(priv, R_CTRL, EN_MASK);
|
arc_reg_clr(priv, R_CTRL, EN_MASK);
|
||||||
|
|
||||||
|
/* Return the sk_buff to system */
|
||||||
|
arc_free_tx_queue(ndev);
|
||||||
|
arc_free_rx_queue(ndev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue