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);