summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2014-03-27 15:40:30 +0000
committerRussell King <rmk+kernel@arm.linux.org.uk>2014-10-17 14:35:38 +0100
commitbfba6f845998dfb17272e64fccd9b2e7e1d88d7b (patch)
treef894353cd24764e946775d1b739361a3113f6da8
parent4ac5dc6cea6751372633c231ba732ac969f8c9b3 (diff)
net:fec: allocate descriptor rings at device open, and free on device close
The driver has had a memory leak for quite some time: when the device is probed, we allocate a page for the descriptor rings, but we never free this. Rather than trying to free it on the various probe failure paths, move its allocation to device open time - we have to restart the FEC for this to take effect. This also means we can free the descriptor rings when we clean up in the device close function, which gives this a nice symmetry. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c81
1 files changed, 39 insertions, 42 deletions
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 93eeb70c644b..a29f392e229d 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -657,7 +657,8 @@ fec_restart(struct net_device *ndev)
/* Set maximum receive buffer size. */
writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE);
- fec_enet_bd_init(ndev);
+ if (fep->rx_bd_base)
+ fec_enet_bd_init(ndev);
netdev_reset_queue(ndev);
/* Set receive and transmit descriptor base. */
@@ -2072,6 +2073,8 @@ static void fec_enet_free_buffers(struct net_device *ndev)
fep->tx_skbuff[i] = NULL;
dev_kfree_skb(skb);
}
+
+ dma_free_coherent(NULL, PAGE_SIZE, fep->rx_bd_base, fep->rx_bd_dma);
}
static int fec_enet_alloc_buffers(struct net_device *ndev)
@@ -2080,6 +2083,37 @@ static int fec_enet_alloc_buffers(struct net_device *ndev)
unsigned int i;
struct sk_buff *skb;
union bufdesc_u *bdp;
+ union bufdesc_u *cbd_base;
+ dma_addr_t cbd_dma;
+ int bd_size;
+
+ bd_size = fep->tx_ring_size + fep->rx_ring_size;
+ if (fep->flags & FEC_FLAG_BUFDESC_EX)
+ bd_size *= sizeof(struct bufdesc_ex);
+ else
+ bd_size *= sizeof(struct bufdesc);
+
+ /* Allocate memory for buffer descriptors. */
+ cbd_base = dma_alloc_coherent(NULL, bd_size, &cbd_dma, GFP_KERNEL);
+ if (!cbd_base)
+ return -ENOMEM;
+
+ memset(cbd_base, 0, PAGE_SIZE);
+
+ /* Set receive and transmit descriptor base. */
+ fep->rx_bd_base = cbd_base;
+ fep->rx_bd_dma = cbd_dma;
+ if (fep->flags & FEC_FLAG_BUFDESC_EX) {
+ fep->tx_bd_base = (union bufdesc_u *)
+ (&cbd_base->ebd + fep->rx_ring_size);
+ fep->tx_bd_dma = cbd_dma + sizeof(struct bufdesc_ex) *
+ fep->rx_ring_size;
+ } else {
+ fep->tx_bd_base = (union bufdesc_u *)
+ (&cbd_base->bd + fep->rx_ring_size);
+ fep->tx_bd_dma = cbd_dma + sizeof(struct bufdesc) *
+ fep->rx_ring_size;
+ }
for (i = 0; i < fep->rx_ring_size; i++) {
dma_addr_t addr;
@@ -2395,36 +2429,16 @@ static const struct net_device_ops fec_netdev_ops = {
.ndo_set_features = fec_set_features,
};
- /*
- * XXX: We need to clean up on failure exits here.
- *
- */
-static int fec_enet_init(struct net_device *ndev)
+static void fec_enet_init(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
const struct platform_device_id *id_entry =
platform_get_device_id(fep->pdev);
- union bufdesc_u *cbd_base;
- dma_addr_t cbd_dma;
- int bd_size;
/* init the tx & rx ring size */
fep->tx_ring_size = TX_RING_SIZE;
fep->rx_ring_size = RX_RING_SIZE;
- bd_size = fep->tx_ring_size + fep->rx_ring_size;
- if (fep->flags & FEC_FLAG_BUFDESC_EX)
- bd_size *= sizeof(struct bufdesc_ex);
- else
- bd_size *= sizeof(struct bufdesc);
-
- /* Allocate memory for buffer descriptors. */
- cbd_base = dma_alloc_coherent(NULL, bd_size, &cbd_dma, GFP_KERNEL);
- if (!cbd_base)
- return -ENOMEM;
-
- memset(cbd_base, 0, PAGE_SIZE);
-
fep->netdev = ndev;
/* Get the Ethernet address */
@@ -2432,20 +2446,8 @@ static int fec_enet_init(struct net_device *ndev)
/* make sure MAC we just acquired is programmed into the hw */
fec_set_mac_address(ndev, NULL);
- /* Set receive and transmit descriptor base. */
- fep->rx_bd_base = cbd_base;
- fep->rx_bd_dma = cbd_dma;
- if (fep->flags & FEC_FLAG_BUFDESC_EX) {
- fep->tx_bd_base = (union bufdesc_u *)
- (&cbd_base->ebd + fep->rx_ring_size);
- fep->tx_bd_dma = cbd_dma + sizeof(struct bufdesc_ex) *
- fep->rx_ring_size;
- } else {
- fep->tx_bd_base = (union bufdesc_u *)
- (&cbd_base->bd + fep->rx_ring_size);
- fep->tx_bd_dma = cbd_dma + sizeof(struct bufdesc) *
- fep->rx_ring_size;
- }
+ fep->rx_bd_base = fep->tx_bd_base = NULL;
+ fep->rx_bd_dma = fep->tx_bd_dma = 0;
/* The FEC Ethernet specific entries in the device structure */
ndev->watchdog_timeo = TX_TIMEOUT;
@@ -2480,8 +2482,6 @@ static int fec_enet_init(struct net_device *ndev)
ndev->hw_features = ndev->features;
fec_restart(ndev);
-
- return 0;
}
#ifdef CONFIG_OF
@@ -2647,9 +2647,7 @@ fec_probe(struct platform_device *pdev)
if (fep->flags & FEC_FLAG_BUFDESC_EX)
fec_ptp_init(pdev);
- ret = fec_enet_init(ndev);
- if (ret)
- goto failed_init;
+ fec_enet_init(ndev);
for (i = 0; i < FEC_IRQ_NUM; i++) {
irq = platform_get_irq(pdev, i);
@@ -2688,7 +2686,6 @@ failed_register:
fec_enet_mii_remove(fep);
failed_mii_init:
failed_irq:
-failed_init:
if (fep->reg_phy)
regulator_disable(fep->reg_phy);
failed_regulator: