diff options
author | Ulf Carlsson <md1ulfc@mdstud.chalmers.se> | 2000-06-16 04:17:15 +0000 |
---|---|---|
committer | Ulf Carlsson <md1ulfc@mdstud.chalmers.se> | 2000-06-16 04:17:15 +0000 |
commit | 847290510f811c572cc2aa80c1f02a04721410b1 (patch) | |
tree | ac8f3122c582b2fbb1bec792854cad457764908e /drivers | |
parent | 5660fe08b83ac8470924ad01f49d36e6779cc689 (diff) |
- Fix memory leak when allocating buffers (we did that twice)
- Free only those rx skb's that we haven't used.
- Forget about old emcr when we shut down the interface.
This makes it possible to bring interfaces down and then up again...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/ioc3-eth.c | 165 |
1 files changed, 104 insertions, 61 deletions
diff --git a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c index 63b0182b7..7342b98b5 100644 --- a/drivers/net/ioc3-eth.c +++ b/drivers/net/ioc3-eth.c @@ -81,19 +81,11 @@ #define SIOCGPARAMS (SIOCDEVPRIVATE+3) /* Read operational parameters */ #define SIOCSPARAMS (SIOCDEVPRIVATE+4) /* Set operational parameters */ -static int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void ioc3_set_multicast_list(struct net_device *dev); -static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev); -static void ioc3_timeout(struct net_device *dev); -static inline unsigned int ioc3_hash(const unsigned char *addr); - -static const char ioc3_str[] = "IOC3 Ethernet"; - /* Private per NIC data of the driver. */ struct ioc3_private { struct ioc3 *regs; int phy; - unsigned long rxr; /* pointer to receiver ring */ + unsigned long *rxr; /* pointer to receiver ring */ struct ioc3_etxd *txr; struct sk_buff *rx_skbs[512]; struct sk_buff *tx_skbs[128]; @@ -107,6 +99,18 @@ struct ioc3_private { spinlock_t ioc3_lock; }; +static int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void ioc3_set_multicast_list(struct net_device *dev); +static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void ioc3_timeout(struct net_device *dev); +static inline unsigned int ioc3_hash(const unsigned char *addr); +static void ioc3_stop(struct net_device *dev); +static void ioc3_clean_tx_ring(struct ioc3_private *ip); +static void ioc3_clean_rx_ring(struct ioc3_private *ip); +static void ioc3_init(struct net_device *dev); + +static const char ioc3_str[] = "IOC3 Ethernet"; + /* We use this to acquire receive skb's that we can DMA directly into. */ #define ALIGNED_RX_SKB_ADDR(addr) \ ((((unsigned long)(addr) + (128 - 1)) & ~(128 - 1)) - (unsigned long)(addr)) @@ -288,7 +292,7 @@ static int nic_init(struct ioc3 *ioc3) type = "unknown"; - do { + while (1) { u64 reg; reg = nic_find(ioc3, &save); @@ -317,7 +321,8 @@ static int nic_init(struct ioc3 *ioc3) reg >>= 8; } crc = reg & 0xff; - } while (0); + break; + } printk("Found %s NIC", type); if (type != "unknown") { @@ -442,21 +447,17 @@ ioc3_rx(struct net_device *dev, struct ioc3_private *ip, struct ioc3 *ioc3) ip->stats.rx_packets++; /* Statistics */ ip->stats.rx_bytes += len; - goto next; - } - if (err & (ERXBUF_CRCERR | ERXBUF_FRAMERR | ERXBUF_CODERR | - ERXBUF_INVPREAMB | ERXBUF_BADPKT | ERXBUF_CARRIER)) { - /* We don't send the skbuf to the network layer, so - just recycle it. */ - new_skb = skb; - - if (err & ERXBUF_CRCERR) /* Statistics */ - ip->stats.rx_crc_errors++; - if (err & ERXBUF_FRAMERR) - ip->stats.rx_frame_errors++; - ip->stats.rx_errors++; + } else { + /* The frame is invalid and the skb never + reached the network layer so we can just + recycle it. */ + new_skb = skb; + ip->stats.rx_errors++; } - + if (err & ERXBUF_CRCERR) /* Statistics */ + ip->stats.rx_crc_errors++; + if (err & ERXBUF_FRAMERR) + ip->stats.rx_frame_errors++; next: ip->rx_skbs[n_entry] = new_skb; rxr[n_entry] = (0xa5UL << 56) | @@ -621,52 +622,70 @@ int ioc3_mii_init(struct net_device *dev, struct ioc3_private *ip, } static void -ioc3_init_rings(struct net_device *dev, struct ioc3_private *ip, - struct ioc3 *ioc3) +ioc3_alloc_rings(struct net_device *dev, struct ioc3_private *ip, + struct ioc3 *ioc3) { struct ioc3_erxbuf *rxb; unsigned long *rxr; - unsigned long ring; int i; - /* Allocate and initialize rx ring. 4kb = 512 entries */ - ip->rxr = get_free_page(GFP_KERNEL); - rxr = (unsigned long *) ip->rxr; + if (ip->rxr == NULL) { + /* Allocate and initialize rx ring. 4kb = 512 entries */ + ip->rxr = (unsigned long *) get_free_page(GFP_KERNEL); + rxr = (unsigned long *) ip->rxr; + + /* Now the rx buffers. The RX ring may be larger but + we only allocate 16 buffers for now. Need to tune + this for performance and memory later. */ + for (i = 0; i < RX_BUFFS; i++) { + struct sk_buff *skb; + + skb = ioc3_alloc_skb(RX_BUF_ALLOC_SIZE, 0); + if (!skb) { + show_free_areas(); + continue; + } - /* Now the rx buffers. The RX ring may be larger but we only - allocate 16 buffers for now. Need to tune this for performance - and memory later. */ - for (i = 0; i < RX_BUFFS; i++) { - struct sk_buff *skb; + ip->rx_skbs[i] = skb; + skb->dev = dev; - skb = ioc3_alloc_skb(RX_BUF_ALLOC_SIZE, 0); - if (!skb) { - show_free_areas(); - continue; + /* Because we reserve afterwards. */ + skb_put(skb, (1664 + RX_OFFSET)); + rxb = (struct ioc3_erxbuf *) skb->data; + rxr[i] = (0xa5UL << 56) + | ((unsigned long) rxb & TO_PHYS_MASK); + skb_reserve(skb, RX_OFFSET); } + ip->rx_ci = 0; + ip->rx_pi = RX_BUFFS; + } - ip->rx_skbs[i] = skb; - skb->dev = dev; - - /* Because we reserve afterwards. */ - skb_put(skb, (1664 + RX_OFFSET)); - rxb = (struct ioc3_erxbuf *) skb->data; - rxb->w0 = 0; /* Clear valid bit */ - rxr[i] = (0xa5UL << 56) | ((unsigned long) rxb & TO_PHYS_MASK); - skb_reserve(skb, RX_OFFSET); + if (ip->txr == NULL) { + /* Allocate and initialize tx rings. 16kb = 128 bufs. */ + ip->txr = (struct ioc3_etxd *)__get_free_pages(GFP_KERNEL, 2); + ip->tx_pi = 0; + ip->tx_ci = 0; } +} + +static void +ioc3_init_rings(struct net_device *dev, struct ioc3_private *ip, + struct ioc3 *ioc3) +{ + unsigned long ring; + + ioc3_alloc_rings(dev, ip, ioc3); + + ioc3_clean_tx_ring(ip); + ioc3_clean_rx_ring(ip); /* Now the rx ring base, consume & produce registers. */ - ring = (0xa5UL << 56) | (ip->rxr & TO_PHYS_MASK); + ring = (0xa5UL << 56) | ((unsigned long)ip->rxr & TO_PHYS_MASK); ioc3->erbr_h = ring >> 32; ioc3->erbr_l = ring & 0xffffffff; - ip->rx_ci = 0; ioc3->ercir = (ip->rx_ci << 3); - ip->rx_pi = RX_BUFFS; ioc3->erpir = (ip->rx_pi << 3) | ERPIR_ARM; - /* Allocate and initialize tx rings. 16kb = 128 bufs. */ - ip->txr = (struct ioc3_etxd *)__get_free_pages(GFP_KERNEL, 2); ring = (0xa5UL << 56) | ((unsigned long)ip->txr & TO_PHYS_MASK); ip->txqlen = 0; /* nothing queued */ @@ -674,10 +693,8 @@ ioc3_init_rings(struct net_device *dev, struct ioc3_private *ip, /* Now the tx ring base, consume & produce registers. */ ioc3->etbr_h = ring >> 32; ioc3->etbr_l = ring & 0xffffffff; - ip->tx_pi = 0; ioc3->etpir = (ip->tx_pi << 7); - ip->tx_ci = 0; - ioc3->etcir = (ip->tx_pi << 7); + ioc3->etcir = (ip->tx_ci << 7); ioc3->etcir; /* Flush */ } @@ -693,24 +710,47 @@ ioc3_clean_tx_ring(struct ioc3_private *ip) ip->tx_skbs[i] = NULL; dev_kfree_skb_any(skb); } + ip->txr[i].cmd = 0; } } static void -ioc3_free_rings(struct ioc3_private *ip) +ioc3_clean_rx_ring(struct ioc3_private *ip) { struct sk_buff *skb; int i; + + for (i = 0; i < RX_BUFFS; i++) { + struct ioc3_erxbuf *rxb; + skb = ip->rx_skbs[i]; + rxb = (struct ioc3_erxbuf *) (skb->data - RX_OFFSET); + + rxb->w0 = 0; + } +} + +static void +ioc3_free_rings(struct ioc3_private *ip) +{ + struct sk_buff *skb; + int rx_entry, n_entry; ioc3_clean_tx_ring(ip); free_pages((unsigned long)ip->txr, 2); + ip->txr = NULL; - for (i=0; i < 512; i++) { - skb = ip->rx_skbs[i]; + n_entry = ip->rx_ci; + rx_entry = ip->rx_pi; + + while (n_entry != rx_entry) { + skb = ip->rx_skbs[n_entry]; if (skb) dev_kfree_skb_any(skb); + + n_entry = (n_entry + 1) & 511; } free_page((unsigned long)ip->rxr); + ip->rxr = NULL; } static inline void @@ -745,7 +785,8 @@ static void ioc3_init(struct net_device *dev) ioc3->emcr = EMCR_RST; /* Reset */ ioc3->emcr; /* flush WB */ udelay(4); /* Give it time ... */ - ioc3->emcr = ip->emcr; + ioc3->emcr = 0; + ioc3->emcr; /* Misc registers */ ioc3->erbar = 0; @@ -767,6 +808,7 @@ static void ioc3_init(struct net_device *dev) ioc3->emcr = ip->emcr; ioc3->eier = EISR_RXTIMERINT | EISR_TXEXPLICIT | /* Interrupts ... */ EISR_RXMEMERR | EISR_TXMEMERR; + ioc3->eier; } static void ioc3_stop(struct net_device *dev) @@ -775,6 +817,7 @@ static void ioc3_stop(struct net_device *dev) struct ioc3 *ioc3 = ip->regs; ioc3->emcr = 0; /* Shutup */ + ip->emcr = 0; ioc3->eier = 0; /* Disable interrupts */ ioc3->eier; /* Flush */ } |