Dave, This is intended for 2.6.16 since it is a new feature. To everyone who has contacted me over the last few months, I apologize for sitting on this for so long (Ive tried to CC a few). I tend to have more time at year end - and so here it is. This patch does not support feature #3 in the thread of discussion at: http://marc.theaimsgroup.com/?t=110712330700001&r=2&w=2 The reason infact i was sitting on it is to try and get #3 out first. That could come later;
while this is for 2.6.16 it should apply cleanly to any of the 2.6.15-rcx trees (infact thats where i tested it). I am going to send an iproute2 patch that is only documentation on usage etc. cheers, jamal
Extends dummy to do IMQ functions with a lot less lines and a lot more stability. Old uses of dummy such as for dial-on-demand continue to work as before. To use the new functionality, you need to turn on qos/classifier actions. The new functionality can be grouped as: 1) qdiscs/policies that are per device as opposed to system wide. This now allows for sharing. 2) Allows for queueing incoming traffic for shaping instead of dropping. I am not aware of any study that shows policing is worse than shaping in achieving the end goal of rate control. I would be interested if anyone is experimenting. Signed-off-by: Jamal Hadi Salim <[EMAIL PROTECTED]> --- drivers/net/dummy.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 215 insertions(+), 5 deletions(-) diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index dd8c15a..6c2a0bf 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -26,8 +26,15 @@ Nick Holloway, 27th May 1994 [I tweaked this explanation a little but that's all] Alan Cox, 30th May 1994 + +*/ +/* + * This driver isnt abused enough ;-> + * Here to add only _just_ a _feeew more_ features, + * 10 years after AC added comment above ;-> hehe - JHS */ + #include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> @@ -35,11 +42,130 @@ #include <linux/etherdevice.h> #include <linux/init.h> #include <linux/moduleparam.h> +#ifdef CONFIG_NET_CLS_ACT +#include <net/pkt_sched.h> +#endif + +#define TX_TIMEOUT (2*HZ) + +#define TX_Q_LIMIT 32 +struct dummy_private { + struct net_device_stats stats; +#ifdef CONFIG_NET_CLS_ACT + struct tasklet_struct dummy_tasklet; + int tasklet_pending; + /* mostly debug stats leave in for now */ + unsigned long stat_te; /* tasklet entered */ + unsigned long stat_tqr; /* transmit queue refill attempt */ + unsigned long stat_rqe; /* receive queue entered */ + unsigned long stat_r2t; /* receive to trasmit transfers */ + unsigned long stat_rqne; /*receiveQ not entered, tasklet resched */ + unsigned long stat_rfe; /* received from egress path */ + unsigned long stat_rfi; /* received from ingress path */ + unsigned long stat_crq; + unsigned long stat_rqw; + struct sk_buff_head rq; + struct sk_buff_head tq; +#endif +}; + +#ifdef CONFIG_NET_CLS_ACT +static void ri_tasklet(unsigned long dev); +#endif + static int numdummies = 1; static int dummy_xmit(struct sk_buff *skb, struct net_device *dev); static struct net_device_stats *dummy_get_stats(struct net_device *dev); +static void dummy_timeout(struct net_device *dev); +static int dummy_open(struct net_device *dev); +static int dummy_close(struct net_device *dev); + +static void dummy_timeout(struct net_device *dev) { + + int cpu = smp_processor_id(); + + dev->trans_start = jiffies; + printk("%s: BUG tx timeout on CPU %d\n",dev->name,cpu); + if (spin_is_locked((&dev->xmit_lock))) + printk("xmit lock grabbed already\n"); + if (spin_is_locked((&dev->queue_lock))) + printk("queue lock grabbed already\n"); +} + +#ifdef CONFIG_NET_CLS_ACT +static void ri_tasklet(unsigned long dev) { + + struct net_device *dv = (struct net_device *)dev; + struct dummy_private *dp = ((struct net_device *)dev)->priv; + struct net_device_stats *stats = &dp->stats; + struct sk_buff *skb = NULL; + + dp->stat_te +=1; + if (NULL == (skb = skb_peek(&dp->tq))) { + dp->stat_tqr +=1; + if (spin_trylock(&dv->xmit_lock)) { + dp->stat_rqe +=1; + while (NULL != (skb = skb_dequeue(&dp->rq))) { + skb_queue_tail(&dp->tq, skb); + dp->stat_r2t +=1; + } + spin_unlock(&dv->xmit_lock); + } else { + /* reschedule */ + dp->stat_rqne +=1; + goto resched; + } + } + + while (NULL != (skb = skb_dequeue(&dp->tq))) { + __u32 from = G_TC_FROM(skb->tc_verd); + + skb->tc_verd = 0; + skb->tc_verd = SET_TC_NCLS(skb->tc_verd); + stats->tx_packets++; + stats->tx_bytes +=skb->len; + if (from & AT_EGRESS) { + dp->stat_rfe +=1; + dev_queue_xmit(skb); + } else if (from & AT_INGRESS) { + + dp->stat_rfi +=1; + netif_rx(skb); + } else { + /* if netfilt is compiled in and packet is + tagged, we could reinject the packet back + this would make it do remaining 10% + of what current IMQ does + if someone really really insists then + this is the spot .. jhs */ + dev_kfree_skb(skb); + stats->tx_dropped++; + } + } + + if (spin_trylock(&dv->xmit_lock)) { + dp->stat_crq +=1; + if (NULL == (skb = skb_peek(&dp->rq))) { + dp->tasklet_pending = 0; + if (netif_queue_stopped(dv)) + netif_wake_queue(dv); + //netif_start_queue(dv); + } else { + dp->stat_rqw +=1; + spin_unlock(&dv->xmit_lock); + goto resched; + } + spin_unlock(&dv->xmit_lock); + } else { +resched: + dp->tasklet_pending = 1; + tasklet_schedule(&dp->dummy_tasklet); + } + +} +#endif static int dummy_set_address(struct net_device *dev, void *p) { @@ -62,12 +188,17 @@ static void __init dummy_setup(struct ne /* Initialize the device structure. */ dev->get_stats = dummy_get_stats; dev->hard_start_xmit = dummy_xmit; + dev->tx_timeout = &dummy_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + dev->open = &dummy_open; + dev->stop = &dummy_close; + dev->set_multicast_list = set_multicast_list; dev->set_mac_address = dummy_set_address; /* Fill in device structure with ethernet-generic values. */ ether_setup(dev); - dev->tx_queue_len = 0; + dev->tx_queue_len = TX_Q_LIMIT; dev->change_mtu = NULL; dev->flags |= IFF_NOARP; dev->flags &= ~IFF_MULTICAST; @@ -77,18 +208,64 @@ static void __init dummy_setup(struct ne static int dummy_xmit(struct sk_buff *skb, struct net_device *dev) { - struct net_device_stats *stats = netdev_priv(dev); + struct dummy_private *dp = ((struct net_device *)dev)->priv; + struct net_device_stats *stats = &dp->stats; + int ret = 0; + { stats->tx_packets++; stats->tx_bytes+=skb->len; + } +#ifdef CONFIG_NET_CLS_ACT + __u32 from = G_TC_FROM(skb->tc_verd); + if (!from || !skb->input_dev ) { +dropped: + dev_kfree_skb(skb); + stats->rx_dropped++; + return ret; + } else { + if (skb->input_dev) + skb->dev = skb->input_dev; + else + printk("warning!!! no idev %s\n",skb->dev->name); + + skb->input_dev = dev; + if (from & AT_INGRESS) { + skb_pull(skb, skb->dev->hard_header_len); + } else { + if (!(from & AT_EGRESS)) { + goto dropped; + } + } + } + if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) { + netif_stop_queue(dev); + } + dev->trans_start = jiffies; + skb_queue_tail(&dp->rq, skb); + if (!dp->tasklet_pending) { + dp->tasklet_pending = 1; + tasklet_schedule(&dp->dummy_tasklet); + } +#else + stats->rx_dropped++; dev_kfree_skb(skb); - return 0; +#endif + return ret; } static struct net_device_stats *dummy_get_stats(struct net_device *dev) { - return netdev_priv(dev); + struct dummy_private *dp = ((struct net_device *)dev)->priv; + struct net_device_stats *stats = &dp->stats; + + pr_debug("tasklets stats %ld:%ld:%ld:%ld:%ld:%ld:%ld:%ld:%ld \n", + dp->stat_rqne, dp->stat_rqw, dp->stat_crq, + dp->stat_te, dp->stat_tqr, dp->stat_rfe, + dp->stat_rfi,dp->stat_rqe, dp->stat_r2t); + + return stats; } static struct net_device **dummies; @@ -97,12 +274,42 @@ static struct net_device **dummies; module_param(numdummies, int, 0); MODULE_PARM_DESC(numdummies, "Number of dummy pseudo devices"); +static int dummy_close(struct net_device *dev) +{ + +#ifdef CONFIG_NET_CLS_ACT + struct dummy_private *dp = ((struct net_device *)dev)->priv; + + tasklet_kill(&dp->dummy_tasklet); + skb_queue_purge(&dp->rq); + skb_queue_purge(&dp->tq); +#endif + netif_stop_queue(dev); + return 0; +} + +static int dummy_open(struct net_device *dev) +{ + +#ifdef CONFIG_NET_CLS_ACT + struct dummy_private *dp = ((struct net_device *)dev)->priv; + + tasklet_init(&dp->dummy_tasklet, ri_tasklet, (unsigned long)dev); + skb_queue_head_init(&dp->rq); + skb_queue_head_init(&dp->tq); +#endif + netif_start_queue(dev); + + return 0; +} + + static int __init dummy_init_one(int index) { struct net_device *dev_dummy; int err; - dev_dummy = alloc_netdev(sizeof(struct net_device_stats), + dev_dummy = alloc_netdev(sizeof(struct dummy_private), "dummy%d", dummy_setup); if (!dev_dummy) @@ -128,6 +335,7 @@ static int __init dummy_init_module(void { int i, err = 0; dummies = kmalloc(numdummies * sizeof(void *), GFP_KERNEL); + if (!dummies) return -ENOMEM; for (i = 0; i < numdummies && !err; i++) @@ -136,12 +344,14 @@ static int __init dummy_init_module(void while (--i >= 0) dummy_free_one(i); } + return err; } static void __exit dummy_cleanup_module(void) { int i; + for (i = 0; i < numdummies; i++) dummy_free_one(i); kfree(dummies);