This patch improves the IP forwarding throughput of the Freescale TSEC/eTSEC Gianfar driver. By recycling the Socket buffer and Data buffer, reduce the unnecessary memory allocation and de-allocation in the forwarding processing chain.
Signed-off-by: Dai Haruki <[EMAIL PROTECTED]> Signed-off-by: Andy Fleming <[EMAIL PROTECTED]> --- drivers/net/Kconfig | 10 + drivers/net/gianfar.c | 351 ++++++++++++++++++++++++++++++++++------- drivers/net/gianfar.h | 60 +++++-- drivers/net/gianfar_ethtool.c | 23 ++- 4 files changed, 357 insertions(+), 87 deletions(-) 9b63ff0fd0d2cf12425e7211f10b571b8bf3c66e diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index bdaaad8..0fe6e74 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2188,6 +2188,16 @@ config GIANFAR config GFAR_NAPI bool "NAPI Support" depends on GIANFAR + help + +config GFAR_SKBUFF_RECYCLING + default y if GIANFAR + bool "Socket Buffer Recycling Support" + depends on GIANFAR + help + This implements a new private socket data buffer recycling algorithm + used for fast IPv4 packet forwarding. Select this if you would like + to improve your latency and throughput performance. config MV643XX_ETH tristate "MV-643XX Ethernet support" diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 218d317..c7b34fa 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -7,6 +7,7 @@ * Based on 8260_io/fcc_enet.c * * Author: Andy Fleming + * Dai Haruki * Maintainer: Kumar Gala * * Copyright (c) 2002-2004 Freescale Semiconductor, Inc. @@ -110,13 +111,13 @@ #define RECEIVE(x) netif_rx(x) #endif const char gfar_driver_name[] = "Gianfar Ethernet"; -const char gfar_driver_version[] = "1.3"; +const char gfar_driver_version[] = "1.4"; static int gfar_enet_open(struct net_device *dev); static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev); static void gfar_timeout(struct net_device *dev); static int gfar_close(struct net_device *dev); -struct sk_buff *gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp); +inline struct sk_buff *gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp); static struct net_device_stats *gfar_get_stats(struct net_device *dev); static int gfar_set_mac_address(struct net_device *dev); static int gfar_change_mtu(struct net_device *dev, int new_mtu); @@ -128,7 +129,7 @@ static void init_registers(struct net_de static int init_phy(struct net_device *dev); static int gfar_probe(struct platform_device *pdev); static int gfar_remove(struct platform_device *pdev); -static void free_skb_resources(struct gfar_private *priv); +static void gfar_free_skb_resources(struct gfar_private *priv); static void gfar_set_multi(struct net_device *dev); static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr); #ifdef CONFIG_GFAR_NAPI @@ -150,6 +151,19 @@ MODULE_AUTHOR("Freescale Semiconductor, MODULE_DESCRIPTION("Gianfar Ethernet Driver"); MODULE_LICENSE("GPL"); +#ifdef CONFIG_GFAR_SKBUFF_RECYCLING +static void gfar_free_recycle_queue(struct gfar_private *priv ); +static inline void gfar_kfree_skb(struct sk_buff *skb, + unsigned long int recyclable); +static void gfar_reset_skb_handler(void* dummy); + +/* + * Local SKB recycling pool (per CPU) + */ +DEFINE_PER_CPU(struct gfar_skb_handler, gfar_skb_handler); + +#endif + /* Returns 1 if incoming frames use an FCB */ static inline int gfar_uses_fcb(struct gfar_private *priv) { @@ -193,7 +207,8 @@ static int gfar_probe(struct platform_de priv->interruptTransmit = platform_get_irq_byname(pdev, "tx"); priv->interruptReceive = platform_get_irq_byname(pdev, "rx"); priv->interruptError = platform_get_irq_byname(pdev, "error"); - if (priv->interruptTransmit < 0 || priv->interruptReceive < 0 || priv->interruptError < 0) + if (priv->interruptTransmit < 0 || priv->interruptReceive < 0 + || priv->interruptError < 0) goto regs_fail; } else { priv->interruptTransmit = platform_get_irq(pdev, 0); @@ -264,7 +279,6 @@ #endif dev->stop = gfar_close; dev->get_stats = gfar_get_stats; dev->change_mtu = gfar_change_mtu; - dev->mtu = 1500; dev->set_multicast_list = gfar_set_multi; dev->ethtool_ops = &gfar_ethtool_ops; @@ -360,6 +374,9 @@ #endif printk("%2.2x%c", dev->dev_addr[idx], idx == 5 ? ' ' : ':'); printk("\n"); + /* Setup MTU */ + gfar_change_mtu(dev, 1500); + /* Even more device info helps when determining which kernel */ /* provided which set of benchmarks. */ #ifdef CONFIG_GFAR_NAPI @@ -370,6 +387,10 @@ #endif printk(KERN_INFO "%s: %d/%d RX/TX BD ring size\n", dev->name, priv->rx_ring_size, priv->tx_ring_size); +#ifdef CONFIG_GFAR_SKBUFF_RECYCLING + printk(KERN_INFO "%s: Socket buffer recycling mode enabled\n", dev->name); +#endif + return 0; register_fail: @@ -409,7 +430,8 @@ static int init_phy(struct net_device *d priv->oldspeed = 0; priv->oldduplex = -1; - snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, priv->einfo->bus_id, priv->einfo->phy_id); + snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, priv->einfo->bus_id, + priv->einfo->phy_id); phydev = phy_connect(dev, phy_id, &adjust_link, 0); @@ -533,7 +555,7 @@ void stop_gfar(struct net_device *dev) free_irq(priv->interruptTransmit, dev); } - free_skb_resources(priv); + gfar_free_skb_resources(priv); dma_free_coherent(NULL, sizeof(struct txbd8)*priv->tx_ring_size @@ -542,14 +564,67 @@ void stop_gfar(struct net_device *dev) gfar_read(®s->tbase0)); } +#ifdef CONFIG_GFAR_SKBUFF_RECYCLING +/* + * function: gfar_reset_skb_handler + * Resetting skb handler spin lock entry in the driver initialization. + * + */ +static void gfar_reset_skb_handler(void* dummy) { + unsigned long flags = 0; + struct gfar_skb_handler* sh = &__get_cpu_var(gfar_skb_handler); + spin_lock_init(&sh->lock); + spin_lock_irqsave(&sh->lock, flags); + sh->recycle_max = GFAR_RECYCLE_MAX; + sh->recycle_count = 0; + sh->recycle_queue = NULL; + spin_unlock_irqrestore(&sh->lock, flags); + printk(KERN_INFO"SKB Handler initialized(max=%d)\n",sh->recycle_max); +} + +/* + * function: gfar_free_recycle_queue + * Reset SKB handler struction and free existance socket buffer + * and data buffer in the recycling queue. + */ +void gfar_free_recycle_queue( struct gfar_private *priv ) +{ + unsigned long flags; + struct sk_buff *clist = NULL; + struct sk_buff *skb; + /* Get recycling queue */ + struct gfar_skb_handler* sh = &__get_cpu_var(gfar_skb_handler); + /* just for making sure there is recycle_queue */ + spin_lock_irqsave(&sh->lock, flags); + if ( (sh->recycle_queue) ) { + /* pick one from head; most recent one */ + clist = sh->recycle_queue; + sh->recycle_count = 0; + sh->recycle_queue = NULL; + } + spin_unlock_irqrestore(&sh->lock, flags); + while (clist) { + skb = clist; + BUG_TRAP(!atomic_read(&skb->users)); + dev_kfree_skb_any(skb); + clist = clist->next; + } + +} +#endif + /* If there are any tx skbs or rx skbs still around, free them. * Then free tx_skbuff and rx_skbuff */ -static void free_skb_resources(struct gfar_private *priv) +static void gfar_free_skb_resources(struct gfar_private *priv) { struct rxbd8 *rxbdp; struct txbd8 *txbdp; int i; + +#ifdef CONFIG_GFAR_SKBUFF_RECYCLING + gfar_free_recycle_queue(priv); +#endif /* Go through all the buffer descriptors and free their data buffers */ txbdp = priv->tx_bd_base; @@ -574,8 +649,8 @@ static void free_skb_resources(struct gf for (i = 0; i < priv->rx_ring_size; i++) { if (priv->rx_skbuff[i]) { dma_unmap_single(NULL, rxbdp->bufPtr, - priv->rx_buffer_size, - DMA_FROM_DEVICE); + priv->rx_buffer_size, + DMA_FROM_DEVICE); dev_kfree_skb_any(priv->rx_skbuff[i]); priv->rx_skbuff[i] = NULL; @@ -778,17 +853,19 @@ int startup_gfar(struct net_device *dev) phy_start(priv->phydev); /* Configure the coalescing support */ + priv->txic = 0; if (priv->txcoalescing) - gfar_write(®s->txic, - mk_ic_value(priv->txcount, priv->txtime)); - else - gfar_write(®s->txic, 0); + priv->txic = mk_ic_value(priv->txcount, priv->txtime); + gfar_write(®s->txic, 0); + gfar_write(®s->txic, priv->txic); + + priv->rxic = 0; if (priv->rxcoalescing) - gfar_write(®s->rxic, - mk_ic_value(priv->rxcount, priv->rxtime)); - else - gfar_write(®s->rxic, 0); + priv->rxic = mk_ic_value(priv->rxcount, priv->rxtime); + + gfar_write(®s->rxic, 0); + gfar_write(®s->rxic, priv->rxic); if (priv->rx_csum_enable) rctrl |= RCTRL_CHECKSUMMING; @@ -847,7 +924,7 @@ tx_irq_fail: free_irq(priv->interruptError, dev); err_irq_fail: rx_skb_fail: - free_skb_resources(priv); + gfar_free_skb_resources(priv); tx_skb_fail: dma_free_coherent(NULL, sizeof(struct txbd8)*priv->tx_ring_size @@ -858,6 +935,7 @@ tx_skb_fail: return err; } + /* Called when something needs to use the ethernet device */ /* Returns 0 for success. */ static int gfar_enet_open(struct net_device *dev) @@ -983,6 +1061,9 @@ static int gfar_start_xmit(struct sk_buf /* in need of CRC */ status |= (TXBD_READY | TXBD_LAST | TXBD_CRC); + /* Tell the DMA to go go go */ + gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT); + dev->trans_start = jiffies; txbdp->status = status; @@ -1005,9 +1086,6 @@ static int gfar_start_xmit(struct sk_buf /* Update the current txbd to the next one */ priv->cur_tx = txbdp; - /* Tell the DMA to go go go */ - gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT); - /* Unlock priv */ spin_unlock_irqrestore(&priv->txlock, flags); @@ -1134,6 +1212,20 @@ static int gfar_change_mtu(struct net_de dev->mtu = new_mtu; +#ifdef CONFIG_GFAR_SKBUFF_RECYCLING + priv->skbuff_truesize = + SKB_DATA_ALIGN(tempsize + RXBUF_ALIGNMENT + + GFAR_SKB_USER_OPT_HEADROOM) + + sizeof(struct sk_buff); + printk(KERN_INFO"%s: MTU = %d (frame size=%d,truesize=%lu)\n", + dev->name,dev->mtu,frame_size, + priv->skbuff_truesize ); +#else + printk(KERN_INFO"%s: MTU = %d (frame size=%d)\n", dev->name, + dev->mtu, frame_size); +#endif /*CONFIG_GFAR_SKBUFF_RECYCLING*/ + + gfar_write(&priv->regs->mrblr, priv->rx_buffer_size); gfar_write(&priv->regs->maxfrm, priv->rx_buffer_size); @@ -1173,18 +1265,13 @@ static void gfar_timeout(struct net_devi netif_schedule(dev); } -/* Interrupt Handler for Transmit complete */ -static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs) +static inline int gfar_clean_tx_ring(struct net_device *dev) { - struct net_device *dev = (struct net_device *) dev_id; - struct gfar_private *priv = netdev_priv(dev); struct txbd8 *bdp; + struct gfar_private *priv = netdev_priv(dev); + int howmany = 0; + struct sk_buff* skb; - /* Clear IEVENT */ - gfar_write(&priv->regs->ievent, IEVENT_TX_MASK); - - /* Lock priv */ - spin_lock(&priv->txlock); bdp = priv->dirty_tx; while ((bdp->status & TXBD_READY) == 0) { /* If dirty_tx and cur_tx are the same, then either the */ @@ -1192,16 +1279,18 @@ static irqreturn_t gfar_transmit(int irq /* obviously). If it is empty, we are done. */ if ((bdp == priv->cur_tx) && (netif_queue_stopped(dev) == 0)) break; - - priv->stats.tx_packets++; - + howmany++; + /* Deferred means some collisions occurred during transmit, */ /* but we eventually sent the packet. */ if (bdp->status & TXBD_DEF) priv->stats.collisions++; - + /* Free the sk buffer associated with this TxBD */ - dev_kfree_skb_irq(priv->tx_skbuff[priv->skb_dirtytx]); + skb = priv->tx_skbuff[priv->skb_dirtytx]; + + GFAR_KFREE_SKB(skb,priv->skbuff_truesize); + priv->tx_skbuff[priv->skb_dirtytx] = NULL; priv->skb_dirtytx = (priv->skb_dirtytx + @@ -1221,19 +1310,144 @@ static irqreturn_t gfar_transmit(int irq netif_wake_queue(dev); } /* while ((bdp->status & TXBD_READY) == 0) */ + priv->stats.tx_packets += howmany; + + return howmany; +} + + +/* Interrupt Handler for Transmit complete */ +static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs) { + struct net_device *dev = (struct net_device *) dev_id; + struct gfar_private *priv = netdev_priv(dev); + + /* Clear IEVENT */ + gfar_write(&priv->regs->ievent, IEVENT_TX_MASK); + + /* Lock priv */ + spin_lock(&priv->txlock); + + gfar_clean_tx_ring(dev); + /* If we are coalescing the interrupts, reset the timer */ - /* Otherwise, clear it */ - if (priv->txcoalescing) - gfar_write(&priv->regs->txic, - mk_ic_value(priv->txcount, priv->txtime)); - else - gfar_write(&priv->regs->txic, 0); + gfar_write(&priv->regs->txic, 0); + gfar_write(&priv->regs->txic, priv->txic); spin_unlock(&priv->txlock); - return IRQ_HANDLED; } + +#ifdef CONFIG_GFAR_SKBUFF_RECYCLING +struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp) +{ + struct gfar_private *priv = netdev_priv(dev); + struct sk_buff *skb = NULL; + unsigned int timeout = SKB_ALLOC_TIMEOUT; + unsigned long flags = 0; + + struct gfar_skb_handler* sh = &__get_cpu_var(gfar_skb_handler); + + spin_lock_irqsave(sh->lock, flags); + if (sh->recycle_queue) { + unsigned int size; + int truesize; + /* pick one from head; most recent one */ + skb = sh->recycle_queue; + sh->recycle_queue = skb->next; + sh->recycle_count--; + spin_unlock_irqrestore(sh->lock, flags); + + /* re-initialization + * We are not going to touch the buffer size, so + * skb->truesize can be used as the truesize again + */ + truesize = skb->truesize; + + size = SKB_DATA_ALIGN(priv->rx_buffer_size + RXBUF_ALIGNMENT + + GFAR_SKB_USER_OPT_HEADROOM); + /* clear structure by &truesize */ + memset(skb, 0, offsetof(struct sk_buff, truesize)); + atomic_set(&skb->users, 1); + /* reserve for optimization. Comply to __dev_alloc_skb() */ + skb->data = skb->head + GFAR_SKB_USER_OPT_HEADROOM; + skb->tail = skb->head + GFAR_SKB_USER_OPT_HEADROOM; + skb->end = skb->head + size; + + /* shared info clean up */ + atomic_set(&(skb_shinfo(skb)->dataref), 1); + skb_shinfo(skb)->nr_frags = 0; + skb_shinfo(skb)->tso_size = 0; + skb_shinfo(skb)->tso_segs = 0; + skb_shinfo(skb)->frag_list = NULL; + + } else { + spin_unlock_irqrestore(sh->lock, flags); + while ((!skb) && timeout--) + skb = dev_alloc_skb(priv->rx_buffer_size + RXBUF_ALIGNMENT); + /* We have to allocate the skb, so keep trying till we succeed */ + if (skb == NULL) + return NULL; + + /* Activate this code if you want to know the recycle backlog*/ + /* printk("newly alloc: count=%d\n",sh->recycle_count); */ + + } + + /* We need the data buffer to be aligned properly. We will reserve + * as many bytes as needed to align the data properly + */ + if (RXBUF_ALIGNMENT != 0) + skb_reserve(skb, + RXBUF_ALIGNMENT - + (((unsigned) skb->data) & (RXBUF_ALIGNMENT - 1))); + + skb->dev = dev; + + bdp->bufPtr = dma_map_single(NULL, skb->data, + priv->rx_buffer_size, + DMA_FROM_DEVICE); + + bdp->length = 0; + + /* Mark the buffer empty */ + bdp->status |= (RXBD_EMPTY | RXBD_INTERRUPT); + + return skb; +} + +static inline void gfar_kfree_skb(struct sk_buff *skb, + unsigned long int recyclable) { + if ((skb->truesize >= recyclable)&& + (!skb->destructor) && (!skb->cloned)) { + if (atomic_dec_and_test(&skb->users)) { + struct gfar_skb_handler *sh; + unsigned long flags = 0; + sh = &__get_cpu_var(gfar_skb_handler); + spin_lock_irqsave(sh->lock, flags); + if (likely(sh->recycle_count < sh->recycle_max)) { + sh->recycle_count++; + skb->next = sh->recycle_queue; + sh->recycle_queue = skb; + } else { + struct softnet_data *sd; + sd = &__get_cpu_var(softnet_data); + skb->next = sd->completion_queue; + sd->completion_queue = skb; + raise_softirq_irqoff(NET_TX_SOFTIRQ); + } + spin_unlock_irqrestore(sh->lock, flags); + } + } else { + /* skb is not recyclable */ + dev_kfree_skb_any(skb); + } +} + +#else +/* + * normal new skb routine + */ struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp) { unsigned int alignamount; @@ -1259,7 +1473,8 @@ struct sk_buff * gfar_new_skb(struct net skb->dev = dev; bdp->bufPtr = dma_map_single(NULL, skb->data, - priv->rx_buffer_size, DMA_FROM_DEVICE); + priv->rx_buffer_size, + DMA_FROM_DEVICE); bdp->length = 0; @@ -1268,6 +1483,7 @@ struct sk_buff * gfar_new_skb(struct net return skb; } +#endif /*CONFIG_GFAR_SKBUFF_RECYCLING*/ static inline void count_errors(unsigned short status, struct gfar_private *priv) { @@ -1324,7 +1540,7 @@ #endif #ifdef CONFIG_GFAR_NAPI if (netif_rx_schedule_prep(dev)) { tempval = gfar_read(&priv->regs->imask); - tempval &= IMASK_RX_DISABLED; + tempval &= IMASK_NAPI_DISABLED; gfar_write(&priv->regs->imask, tempval); __netif_rx_schedule(dev); @@ -1334,21 +1550,18 @@ #ifdef CONFIG_GFAR_NAPI dev->name, gfar_read(&priv->regs->ievent), gfar_read(&priv->regs->imask)); } -#else +#else /* CONFIG_GFAR_NAPI */ spin_lock_irqsave(&priv->rxlock, flags); gfar_clean_rx_ring(dev, priv->rx_ring_size); /* If we are coalescing interrupts, update the timer */ - /* Otherwise, clear it */ + gfar_write(&priv->regs->rxic, 0); if (priv->rxcoalescing) - gfar_write(&priv->regs->rxic, - mk_ic_value(priv->rxcount, priv->rxtime)); - else - gfar_write(&priv->regs->rxic, 0); + gfar_write(&priv->regs->rxic, priv->rxic); spin_unlock_irqrestore(&priv->rxlock, flags); -#endif +#endif /* CONFIG_GFAR_NAPI */ return IRQ_HANDLED; } @@ -1509,8 +1722,11 @@ static int gfar_poll(struct net_device * if (rx_work_limit > dev->quota) rx_work_limit = dev->quota; + howmany = gfar_clean_rx_ring(dev, rx_work_limit); + gfar_clean_tx_ring(dev); + dev->quota -= howmany; rx_work_limit -= howmany; *budget -= howmany; @@ -1521,15 +1737,14 @@ static int gfar_poll(struct net_device * /* Clear the halt bit in RSTAT */ gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT); - gfar_write(&priv->regs->imask, IMASK_DEFAULT); - /* If we are coalescing interrupts, update the timer */ /* Otherwise, clear it */ + gfar_write(&priv->regs->rxic, 0); if (priv->rxcoalescing) - gfar_write(&priv->regs->rxic, - mk_ic_value(priv->rxcount, priv->rxtime)); - else - gfar_write(&priv->regs->rxic, 0); + gfar_write(&priv->regs->rxic, priv->rxic); + + gfar_write(&priv->regs->imask, IMASK_DEFAULT); + } /* Return 1 if there's more work to do */ @@ -1542,6 +1757,7 @@ static irqreturn_t gfar_interrupt(int ir { struct net_device *dev = dev_id; struct gfar_private *priv = netdev_priv(dev); + unsigned long tempval; /* Save ievent for future reference */ u32 events = gfar_read(&priv->regs->ievent); @@ -1567,13 +1783,19 @@ static irqreturn_t gfar_interrupt(int ir priv->stats.tx_aborted_errors++; if (events & IEVENT_XFUN) { if (netif_msg_tx_err(priv)) - printk(KERN_WARNING "%s: tx underrun. dropped packet\n", dev->name); + printk(KERN_WARNING + "%s: tx underrun. dropped packet\n", + dev->name); priv->stats.tx_dropped++; priv->extra_stats.tx_underrun++; /* Reactivate the Tx Queues */ gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT); } + /* Make sure we aren't stopped */ + tempval = gfar_read(&priv->regs->dmactrl); + tempval &= ~(DMACTRL_GRS | DMACTRL_GTS); + gfar_write(&priv->regs->dmactrl, tempval); } if (events & IEVENT_BSY) { priv->stats.rx_errors++; @@ -1600,6 +1822,8 @@ #endif } if (events & IEVENT_EBERR) { priv->extra_stats.eberr++; + /* Reactivate the Tx Queues */ + gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT); if (netif_msg_rx_err(priv)) printk(KERN_DEBUG "%s: EBERR\n", dev->name); } @@ -1668,8 +1892,9 @@ static void adjust_link(struct net_devic default: if (netif_msg_link(priv)) printk(KERN_WARNING - "%s: Ack! Speed (%d) is not 10/100/1000!\n", - dev->name, phydev->speed); + "%s: Ack! Speed (%d) is " + "not 10/100/1000!\n", + dev->name, phydev->speed); break; } @@ -1958,7 +2183,9 @@ static int __init gfar_init(void) if (err) gfar_mdio_exit(); - + + on_each_cpu(gfar_reset_skb_handler, NULL, 0, 1); + return err; } diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index 127c98c..31c788a 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -6,6 +6,7 @@ * Based on 8260_io/fcc_enet.c * * Author: Andy Fleming + * Dai Haruki * Maintainer: Kumar Gala * * Copyright (c) 2002-2004 Freescale Semiconductor, Inc. @@ -51,7 +52,7 @@ #include <linux/fsl_devices.h> #include "gianfar_mii.h" /* The maximum number of packets to be handled in one call of gfar_poll */ -#define GFAR_DEV_WEIGHT 64 +#define GFAR_DEV_WEIGHT 16 /* Length for FCB */ #define GMAC_FCB_LEN 8 @@ -62,6 +63,8 @@ #define DEFAULT_PADDING 2 /* Number of bytes to align the rx bufs to */ #define RXBUF_ALIGNMENT 64 +#define GFAR_SKB_USER_OPT_HEADROOM 16 + /* The number of bytes which composes a unit for the purpose of * allocating data buffers. ie-for any given MTU, the data buffer * will be the next highest multiple of 512 bytes. */ @@ -73,15 +76,15 @@ #define MAC_ADDR_LEN 6 #define PHY_INIT_TIMEOUT 100000 #define GFAR_PHY_CHANGE_TIME 2 -#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.2, " +#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.4, " #define DRV_NAME "gfar-enet" extern const char gfar_driver_name[]; extern const char gfar_driver_version[]; /* These need to be powers of 2 for this driver */ #ifdef CONFIG_GFAR_NAPI -#define DEFAULT_TX_RING_SIZE 256 -#define DEFAULT_RX_RING_SIZE 256 +#define DEFAULT_TX_RING_SIZE 64 +#define DEFAULT_RX_RING_SIZE 64 #else #define DEFAULT_TX_RING_SIZE 64 #define DEFAULT_RX_RING_SIZE 64 @@ -101,10 +104,10 @@ #define JUMBO_BUFFER_SIZE 9728 #define JUMBO_FRAME_SIZE 9600 #define DEFAULT_FIFO_TX_THR 0x100 -#define DEFAULT_FIFO_TX_STARVE 0x40 -#define DEFAULT_FIFO_TX_STARVE_OFF 0x80 +#define DEFAULT_FIFO_TX_STARVE 0x80 +#define DEFAULT_FIFO_TX_STARVE_OFF 0x100 #define DEFAULT_BD_STASH 1 -#define DEFAULT_STASH_LENGTH 64 +#define DEFAULT_STASH_LENGTH 96 #define DEFAULT_STASH_INDEX 0 /* The number of Exact Match registers */ @@ -125,12 +128,12 @@ #define GFAR_100_TIME 2560 #define GFAR_10_TIME 25600 #define DEFAULT_TX_COALESCE 1 -#define DEFAULT_TXCOUNT 16 -#define DEFAULT_TXTIME 4 +#define DEFAULT_TXCOUNT 24 +#define DEFAULT_TXTIME 64 #define DEFAULT_RX_COALESCE 1 -#define DEFAULT_RXCOUNT 16 -#define DEFAULT_RXTIME 4 +#define DEFAULT_RXCOUNT 2 +#define DEFAULT_RXTIME 64 #define TBIPA_VALUE 0x1f #define MIIMCFG_INIT_VALUE 0x00000007 @@ -235,6 +238,7 @@ #define IEVENT_DPE 0x00000002 #define IEVENT_PERR 0x00000001 #define IEVENT_RX_MASK (IEVENT_RXB0 | IEVENT_RXF0) #define IEVENT_TX_MASK (IEVENT_TXB | IEVENT_TXF) +#define IEVENT_RTX_MASK (IEVENT_RX_MASK | IEVENT_TX_MASK) #define IEVENT_ERR_MASK \ (IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \ IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \ @@ -262,11 +266,11 @@ #define IMASK_FIR 0x00000008 #define IMASK_FIQ 0x00000004 #define IMASK_DPE 0x00000002 #define IMASK_PERR 0x00000001 -#define IMASK_RX_DISABLED ~(IMASK_RXFEN0 | IMASK_BSY) -#define IMASK_DEFAULT (IMASK_TXEEN | IMASK_TXFEN | IMASK_TXBEN | \ +#define IMASK_NAPI_DISABLED ~(IMASK_RXFEN0 | IMASK_BSY | IMASK_TXFEN) +#define IMASK_DEFAULT (IMASK_TXEEN | IMASK_TXFEN | \ IMASK_RXFEN0 | IMASK_BSY | IMASK_EBERR | IMASK_BABR | \ - IMASK_XFUN | IMASK_RXC | IMASK_BABT | IMASK_DPE \ - | IMASK_PERR) + IMASK_XFUN | IMASK_RXC | IMASK_BABT | \ + IMASK_DPE | IMASK_PERR) /* Fifo management */ #define FIFO_TX_THR_MASK 0x01ff @@ -691,6 +695,13 @@ struct gfar_private { unsigned char rxcoalescing; unsigned short rxcount; unsigned short rxtime; + unsigned long txic; + unsigned long rxic; + /* Buffer size for Advanced SKB Handling */ +#ifdef CONFIG_GFAR_SKBUFF_RECYCLING + unsigned long skbuff_truesize; + unsigned long skbuff_buffsize; +#endif struct rxbd8 *rx_bd_base; /* First Rx buffers */ struct rxbd8 *cur_rx; /* Next free rx ring entry */ @@ -765,4 +776,23 @@ extern void gfar_phy_test(struct mii_bus int enable, u32 regnum, u32 read); void gfar_init_sysfs(struct net_device *dev); + +#ifdef CONFIG_GFAR_SKBUFF_RECYCLING +#define GFAR_RECYCLE_MAX 64 +struct gfar_skb_handler { + spinlock_t lock; + long int recycle_max; + long int recycle_count; /* should be atomic */ + struct sk_buff *recycle_queue; +}; + +DECLARE_PER_CPU(struct gfar_skb_handler, gfar_skb_handler); + +#define GFAR_KFREE_SKB(skb,size) gfar_kfree_skb( skb, size ) + +#else /*CONFIG_GFAR_SKBUFF_RECYCLING*/ +/* use dev_kfree_skb_irq directly */ +#define GFAR_KFREE_SKB(skb,arg...) dev_kfree_skb_irq( skb ) +#endif /*CONFIG_GFAR_SKBUFF_RECYCLING*/ + #endif /* __GIANFAR_H */ diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index d69698c..026f07c 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c @@ -363,6 +363,10 @@ static int gfar_scoalesce(struct net_dev priv->rxtime = gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs); priv->rxcount = cvals->rx_max_coalesced_frames; + if (priv->rxcoalescing) + priv->rxic = mk_ic_value(priv->rxcount, priv->rxtime); + else + priv->rxic = 0; /* Set up tx coalescing */ if ((cvals->tx_coalesce_usecs == 0) || @@ -371,6 +375,11 @@ static int gfar_scoalesce(struct net_dev else priv->txcoalescing = 1; + if (priv->txcoalescing) + priv->txic = mk_ic_value(priv->txcount, priv->txtime); + else + priv->txic = 0; + /* Check the bounds of the values */ if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) { pr_info("Coalescing is limited to %d microseconds\n", @@ -387,17 +396,11 @@ static int gfar_scoalesce(struct net_dev priv->txtime = gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs); priv->txcount = cvals->tx_max_coalesced_frames; - if (priv->rxcoalescing) - gfar_write(&priv->regs->rxic, - mk_ic_value(priv->rxcount, priv->rxtime)); - else - gfar_write(&priv->regs->rxic, 0); + gfar_write(&priv->regs->rxic, 0); + gfar_write(&priv->regs->rxic, priv->rxic); - if (priv->txcoalescing) - gfar_write(&priv->regs->txic, - mk_ic_value(priv->txcount, priv->txtime)); - else - gfar_write(&priv->regs->txic, 0); + gfar_write(&priv->regs->txic, 0); + gfar_write(&priv->regs->txic, priv->txic); return 0; } -- 1.3.1.g7464-dirty - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html