summaryrefslogtreecommitdiffstats
path: root/net/sched
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-02-05 06:47:02 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-02-05 06:47:02 +0000
commit99a7e12f34b3661a0d1354eef83a0eef4df5e34c (patch)
tree3560aca9ca86792f9ab7bd87861ea143a1b3c7a3 /net/sched
parente73a04659c0b8cdee4dd40e58630e2cf63afb316 (diff)
Merge with Linux 2.3.38.
Diffstat (limited to 'net/sched')
-rw-r--r--net/sched/Config.in42
-rw-r--r--net/sched/Makefile32
-rw-r--r--net/sched/cls_api.c3
-rw-r--r--net/sched/cls_tcindex.c503
-rw-r--r--net/sched/sch_api.c87
-rw-r--r--net/sched/sch_dsmark.c476
-rw-r--r--net/sched/sch_generic.c8
-rw-r--r--net/sched/sch_gred.c606
-rw-r--r--net/sched/sch_ingress.c392
-rw-r--r--net/sched/sch_prio.c13
-rw-r--r--net/sched/sch_teql.c2
11 files changed, 2113 insertions, 51 deletions
diff --git a/net/sched/Config.in b/net/sched/Config.in
index aeb2141bb..f1d9059f4 100644
--- a/net/sched/Config.in
+++ b/net/sched/Config.in
@@ -3,34 +3,38 @@
#
define_bool CONFIG_NETLINK y
define_bool CONFIG_RTNETLINK y
-tristate 'CBQ packet scheduler' CONFIG_NET_SCH_CBQ
-tristate 'CSZ packet scheduler' CONFIG_NET_SCH_CSZ
-#tristate 'H-PFQ packet scheduler' CONFIG_NET_SCH_HPFQ
-#tristate 'H-FSC packet scheduler' CONFIG_NET_SCH_HFCS
+tristate ' CBQ packet scheduler' CONFIG_NET_SCH_CBQ
+tristate ' CSZ packet scheduler' CONFIG_NET_SCH_CSZ
+#tristate ' H-PFQ packet scheduler' CONFIG_NET_SCH_HPFQ
+#tristate ' H-FSC packet scheduler' CONFIG_NET_SCH_HFCS
if [ "$CONFIG_ATM" = "y" ]; then
- bool 'ATM pseudo-scheduler' CONFIG_NET_SCH_ATM
+ bool ' ATM pseudo-scheduler' CONFIG_NET_SCH_ATM
fi
-tristate 'The simplest PRIO pseudoscheduler' CONFIG_NET_SCH_PRIO
-tristate 'RED queue' CONFIG_NET_SCH_RED
-tristate 'SFQ queue' CONFIG_NET_SCH_SFQ
-tristate 'TEQL queue' CONFIG_NET_SCH_TEQL
-tristate 'TBF queue' CONFIG_NET_SCH_TBF
-bool 'QoS support' CONFIG_NET_QOS
+tristate ' The simplest PRIO pseudoscheduler' CONFIG_NET_SCH_PRIO
+tristate ' RED queue' CONFIG_NET_SCH_RED
+tristate ' SFQ queue' CONFIG_NET_SCH_SFQ
+tristate ' TEQL queue' CONFIG_NET_SCH_TEQL
+tristate ' TBF queue' CONFIG_NET_SCH_TBF
+tristate ' GRED queue' CONFIG_NET_SCH_GRED
+tristate ' Diffserv field marker' CONFIG_NET_SCH_DSMARK
+tristate ' Ingress Qdisc' CONFIG_NET_SCH_INGRESS
+bool ' QoS support' CONFIG_NET_QOS
if [ "$CONFIG_NET_QOS" = "y" ]; then
- bool ' Rate estimator' CONFIG_NET_ESTIMATOR
+ bool ' Rate estimator' CONFIG_NET_ESTIMATOR
fi
-bool 'Packet classifier API' CONFIG_NET_CLS
+bool ' Packet classifier API' CONFIG_NET_CLS
if [ "$CONFIG_NET_CLS" = "y" ]; then
- tristate ' Routing table based classifier' CONFIG_NET_CLS_ROUTE4
+ tristate ' TC index classifier' CONFIG_NET_CLS_TCINDEX
+ tristate ' Routing table based classifier' CONFIG_NET_CLS_ROUTE4
if [ "$CONFIG_NET_CLS_ROUTE4" != "n" ]; then
define_bool CONFIG_NET_CLS_ROUTE y
fi
- tristate ' Firewall based classifier' CONFIG_NET_CLS_FW
- tristate ' U32 classifier' CONFIG_NET_CLS_U32
+ tristate ' Firewall based classifier' CONFIG_NET_CLS_FW
+ tristate ' U32 classifier' CONFIG_NET_CLS_U32
if [ "$CONFIG_NET_QOS" = "y" ]; then
- tristate ' Special RSVP classifier' CONFIG_NET_CLS_RSVP
- tristate ' Special RSVP classifier for IPv6' CONFIG_NET_CLS_RSVP6
- bool ' Ingres traffic policing' CONFIG_NET_CLS_POLICE
+ tristate ' Special RSVP classifier' CONFIG_NET_CLS_RSVP
+ tristate ' Special RSVP classifier for IPv6' CONFIG_NET_CLS_RSVP6
+ bool ' Traffic policing (needed for in/egress)' CONFIG_NET_CLS_POLICE
fi
fi
diff --git a/net/sched/Makefile b/net/sched/Makefile
index 5c96ebe82..de1f71601 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -28,6 +28,14 @@ endif
endif
+ifeq ($(CONFIG_NET_SCH_INGRESS), y)
+O_OBJS += sch_ingress.o
+else
+ ifeq ($(CONFIG_NET_SCH_INGRESS), m)
+ M_OBJS += sch_ingress.o
+ endif
+endif
+
ifeq ($(CONFIG_NET_SCH_CBQ), y)
O_OBJS += sch_cbq.o
else
@@ -101,6 +109,30 @@ else
endif
endif
+ifeq ($(CONFIG_NET_SCH_GRED), y)
+O_OBJS += sch_gred.o
+else
+ ifeq ($(CONFIG_NET_SCH_GRED), m)
+ M_OBJS += sch_gred.o
+ endif
+endif
+
+ifeq ($(CONFIG_NET_SCH_DSMARK), y)
+O_OBJS += sch_dsmark.o
+else
+ ifeq ($(CONFIG_NET_SCH_DSMARK), m)
+ M_OBJS += sch_dsmark.o
+ endif
+endif
+
+ifeq ($(CONFIG_NET_CLS_TCINDEX), y)
+O_OBJS += cls_tcindex.o
+else
+ ifeq ($(CONFIG_NET_CLS_TCINDEX), m)
+ M_OBJS += cls_tcindex.o
+ endif
+endif
+
ifeq ($(CONFIG_NET_SCH_ATM), y)
O_OBJS += sch_atm.o
endif
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index d77a8daf2..2632cee0f 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -466,6 +466,9 @@ int __init tc_filter_init(void)
#ifdef CONFIG_NET_CLS_RSVP
INIT_TC_FILTER(rsvp);
#endif
+#ifdef CONFIG_NET_CLS_TCINDEX
+ INIT_TC_FILTER(tcindex);
+#endif
#ifdef CONFIG_NET_CLS_RSVP6
INIT_TC_FILTER(rsvp6);
#endif
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c
new file mode 100644
index 000000000..5aa6133cd
--- /dev/null
+++ b/net/sched/cls_tcindex.c
@@ -0,0 +1,503 @@
+/*
+ * net/sched/cls_tcindex.c Packet classifier for skb->tc_index
+ *
+ * Written 1998,1999 by Werner Almesberger, EPFL ICA
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+#include <net/pkt_sched.h>
+#include <net/route.h>
+
+
+/*
+ * Not quite sure if we need all the xchgs Alexey uses when accessing things.
+ * Can always add them later ... :)
+ */
+
+/*
+ * Passing parameters to the root seems to be done more awkwardly than really
+ * necessary. At least, u32 doesn't seem to use such dirty hacks. To be
+ * verified. FIXME.
+ */
+
+#define PERFECT_HASH_THRESHOLD 64 /* use perfect hash if not bigger */
+#define DEFAULT_HASH_SIZE 64 /* optimized for diffserv */
+
+
+#if 1 /* control */
+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define DPRINTK(format,args...)
+#endif
+
+#if 0 /* data */
+#define D2PRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define D2PRINTK(format,args...)
+#endif
+
+
+#define PRIV(tp) ((struct tcindex_data *) (tp)->root)
+
+
+struct tcindex_filter_result {
+ struct tcf_police *police;
+ struct tcf_result res;
+};
+
+struct tcindex_filter {
+ __u16 key;
+ struct tcindex_filter_result result;
+ struct tcindex_filter *next;
+};
+
+
+struct tcindex_data {
+ struct tcindex_filter_result *perfect; /* perfect hash; NULL if none */
+ struct tcindex_filter **h; /* imperfect hash; only used if !perfect;
+ NULL if unused */
+ __u16 mask; /* AND key with mask */
+ int shift; /* shift ANDed key to the right */
+ int hash; /* hash table size; 0 if undefined */
+ int alloc_hash; /* allocated size */
+ int fall_through; /* 0: only classify if explicit match */
+};
+
+
+static struct tcindex_filter_result *lookup(struct tcindex_data *p,__u16 key)
+{
+ struct tcindex_filter *f;
+
+ if (p->perfect)
+ return p->perfect[key].res.classid ? p->perfect+key : NULL;
+ if (!p->h)
+ return NULL;
+ for (f = p->h[key % p->hash]; f; f = f->next) {
+ if (f->key == key)
+ return &f->result;
+ }
+ return NULL;
+}
+
+
+static int tcindex_classify(struct sk_buff *skb, struct tcf_proto *tp,
+ struct tcf_result *res)
+{
+ struct tcindex_data *p = PRIV(tp);
+ struct tcindex_filter_result *f;
+
+ D2PRINTK("tcindex_classify(skb %p,tp %p,res %p),p %p\n",skb,tp,res,p);
+
+ f = lookup(p,(skb->tc_index & p->mask) >> p->shift);
+ if (!f) {
+ if (!p->fall_through)
+ return -1;
+ res->classid = TC_H_MAKE(TC_H_MAJ(tp->q->handle),
+ (skb->tc_index& p->mask) >> p->shift);
+ res->class = 0;
+ D2PRINTK("alg 0x%x\n",res->classid);
+ return 0;
+ }
+ *res = f->res;
+ D2PRINTK("map 0x%x\n",res->classid);
+#ifdef CONFIG_NET_CLS_POLICE
+ if (f->police) {
+ int result;
+
+ result = tcf_police(skb,f->police);
+ D2PRINTK("police %d\n",res);
+ return result;
+ }
+#endif
+ return 0;
+}
+
+
+static unsigned long tcindex_get(struct tcf_proto *tp, u32 handle)
+{
+ DPRINTK("tcindex_get(tp %p,handle 0x%08x)\n",tp,handle);
+ return (unsigned long) lookup(PRIV(tp),handle);
+}
+
+
+static void tcindex_put(struct tcf_proto *tp, unsigned long f)
+{
+ DPRINTK("tcindex_put(tp %p,f 0x%lx)\n",tp,f);
+}
+
+
+static int tcindex_init(struct tcf_proto *tp)
+{
+ struct tcindex_data *p;
+
+ DPRINTK("tcindex_init(tp %p)\n",tp);
+ MOD_INC_USE_COUNT;
+ p = kmalloc(sizeof(struct tcindex_data),GFP_KERNEL);
+ if (!p) {
+ MOD_DEC_USE_COUNT;
+ return -ENOMEM;
+ }
+ tp->root = p;
+ p->perfect = NULL;
+ p->h = NULL;
+ p->hash = 0;
+ p->mask = 0xffff;
+ p->shift = 0;
+ p->fall_through = 1;
+ return 0;
+}
+
+
+static int tcindex_delete(struct tcf_proto *tp, unsigned long arg)
+{
+ struct tcindex_data *p = PRIV(tp);
+ struct tcindex_filter_result *r = (struct tcindex_filter_result *) arg;
+ struct tcindex_filter *f = NULL;
+ unsigned long cl;
+
+ DPRINTK("tcindex_delete(tp %p,arg 0x%lx),p %p,f %p\n",tp,arg,p,f);
+ if (p->perfect) {
+ if (!r->res.classid)
+ return -ENOENT;
+ } else {
+ int i;
+ struct tcindex_filter **walk = NULL;
+
+ for (i = 0; !f && i < p->hash; i++) {
+ for (walk = p->h+i; !f && *walk; walk = &(*walk)->next) {
+ if (&(*walk)->result == r)
+ f = *walk;
+ }
+ }
+ if (!f)
+ return -ENOENT;
+/*
+ @@@ OK? -- No (jhs)
+Look more into it
+ tcf_tree_lock(tp);
+*/
+ *walk = f->next;
+/*
+ tcf_tree_unlock(tp);
+*/
+ }
+ cl = __cls_set_class(&r->res.class,0);
+ if (cl)
+ tp->q->ops->cl_ops->unbind_tcf(tp->q,cl);
+#ifdef CONFIG_NET_CLS_POLICE
+ tcf_police_release(r->police);
+#endif
+ if (f)
+ kfree(f);
+ return 0;
+}
+
+
+/*
+ * There are no parameters for tcindex_init, so we overload tcindex_change
+ */
+
+
+static int tcindex_change(struct tcf_proto *tp,unsigned long base,u32 handle,
+ struct rtattr **tca,unsigned long *arg)
+{
+ struct tcindex_filter_result new_filter_result = {
+ NULL, /* no policing */
+ { 0,0 }, /* no classification */
+ };
+ struct rtattr *opt = tca[TCA_OPTIONS-1];
+ struct rtattr *tb[TCA_TCINDEX_MAX];
+ struct tcindex_data *p = PRIV(tp);
+ struct tcindex_filter *f;
+ struct tcindex_filter_result *r = (struct tcindex_filter_result *) *arg;
+ struct tcindex_filter **walk;
+ int hash;
+ __u16 mask;
+
+ DPRINTK("tcindex_change(tp %p,handle 0x%08x,tca %p,arg %p),opt %p,"
+ "p %p,r %p\n",tp,handle,tca,arg,opt,p,r);
+ if (arg)
+ DPRINTK("*arg = 0x%lx\n",*arg);
+ if (!opt)
+ return 0;
+ if (rtattr_parse(tb,TCA_TCINDEX_MAX,RTA_DATA(opt),RTA_PAYLOAD(opt)) < 0)
+ return -EINVAL;
+ if (!tb[TCA_TCINDEX_HASH-1]) {
+ hash = p->hash;
+ } else {
+ if (RTA_PAYLOAD(tb[TCA_TCINDEX_HASH-1]) < sizeof(int))
+ return -EINVAL;
+ hash = *(int *) RTA_DATA(tb[TCA_TCINDEX_HASH-1]);
+ }
+ if (!tb[TCA_TCINDEX_MASK-1]) {
+ mask = p->mask;
+ } else {
+ if (RTA_PAYLOAD(tb[TCA_TCINDEX_MASK-1]) < sizeof(__u16))
+ return -EINVAL;
+ mask = *(__u16 *) RTA_DATA(tb[TCA_TCINDEX_MASK-1]);
+ }
+ if (p->perfect && hash <= mask)
+ return -EBUSY;
+ if ((p->perfect || p->h) && hash > p->alloc_hash)
+ return -EBUSY;
+ p->hash = hash;
+ p->mask = mask;
+ if (tb[TCA_TCINDEX_SHIFT-1]) {
+ if (RTA_PAYLOAD(tb[TCA_TCINDEX_SHIFT-1]) < sizeof(__u16))
+ return -EINVAL;
+ p->shift = *(int *) RTA_DATA(tb[TCA_TCINDEX_SHIFT-1]);
+ }
+ if (tb[TCA_TCINDEX_FALL_THROUGH-1]) {
+ if (RTA_PAYLOAD(tb[TCA_TCINDEX_FALL_THROUGH-1]) < sizeof(int))
+ return -EINVAL;
+ p->fall_through =
+ *(int *) RTA_DATA(tb[TCA_TCINDEX_FALL_THROUGH-1]);
+ }
+ DPRINTK("classid/police %p/%p\n",tb[TCA_TCINDEX_CLASSID-1],
+ tb[TCA_TCINDEX_POLICE-1]);
+ if (!tb[TCA_TCINDEX_CLASSID-1] && !tb[TCA_TCINDEX_POLICE-1])
+ return 0;
+ if (!p->hash) {
+ if (p->mask < PERFECT_HASH_THRESHOLD) {
+ p->hash = p->mask+1;
+ } else {
+ p->hash = DEFAULT_HASH_SIZE;
+ }
+ }
+ if (!p->perfect && !p->h) {
+ p->alloc_hash = p->hash;
+ DPRINTK("hash %d mask %d\n",p->hash,p->mask);
+ if (p->hash > p->mask) {
+ p->perfect = kmalloc(p->hash*
+ sizeof(struct tcindex_filter_result),GFP_KERNEL);
+ if (!p->perfect)
+ return -ENOMEM;
+ memset(p->perfect, 0,
+ p->hash * sizeof(struct tcindex_filter_result));
+ } else {
+ p->h = kmalloc(p->hash*sizeof(struct tcindex_filter *),
+ GFP_KERNEL);
+ if (!p->h)
+ return -ENOMEM;
+ memset(p->h, 0, p->hash*sizeof(struct tcindex_filter *));
+ }
+ }
+ if (handle > p->mask)
+ return -EINVAL;
+ if (p->perfect) {
+ r = p->perfect+handle;
+ } else {
+ r = lookup(p,handle);
+ DPRINTK("r=%p\n",r);
+ if (!r)
+ r = &new_filter_result;
+ }
+ DPRINTK("r=%p\n",r);
+ if (tb[TCA_TCINDEX_CLASSID-1]) {
+ unsigned long cl = cls_set_class(tp,&r->res.class,0);
+
+ if (cl)
+ tp->q->ops->cl_ops->unbind_tcf(tp->q,cl);
+ r->res.classid = *(__u32 *) RTA_DATA(tb[TCA_TCINDEX_CLASSID-1]);
+ r->res.class = tp->q->ops->cl_ops->bind_tcf(tp->q,base,
+ r->res.classid);
+ if (!r->res.class) {
+ r->res.classid = 0;
+ return -ENOENT;
+ }
+ }
+#ifdef CONFIG_NET_CLS_POLICE
+ if (!tb[TCA_TCINDEX_POLICE-1]) {
+ r->police = NULL;
+ } else {
+ struct tcf_police *police =
+ tcf_police_locate(tb[TCA_TCINDEX_POLICE-1],NULL);
+
+ tcf_tree_lock(tp);
+ police = xchg(&r->police,police);
+ tcf_tree_unlock(tp);
+ tcf_police_release(police);
+ }
+#endif
+ if (r != &new_filter_result)
+ return 0;
+ f = kmalloc(sizeof(struct tcindex_filter),GFP_KERNEL);
+ if (!f)
+ return -ENOMEM;
+ f->key = handle;
+ f->result = new_filter_result;
+ f->next = NULL;
+ for (walk = p->h+(handle % p->hash); *walk; walk = &(*walk)->next)
+ /* nothing */;
+ wmb();
+ *walk = f;
+ return 0;
+}
+
+
+static void tcindex_walk(struct tcf_proto *tp, struct tcf_walker *walker)
+{
+ struct tcindex_data *p = PRIV(tp);
+ struct tcindex_filter *f;
+ int i;
+
+ DPRINTK("tcindex_walk(tp %p,walker %p),p %p\n",tp,walker,p);
+ if (p->perfect) {
+ for (i = 0; i < p->hash; i++) {
+ if (!p->perfect[i].res.classid)
+ continue;
+ if (walker->count >= walker->skip) {
+ if (walker->fn(tp,
+ (unsigned long) (p->perfect+i), walker)
+ < 0) {
+ walker->stop = 1;
+ return;
+ }
+ }
+ walker->count++;
+ }
+ }
+ if (!p->h)
+ return;
+ for (i = 0; i < p->hash; i++) {
+ for (f = p->h[i]; f; f = f->next) {
+ if (walker->count >= walker->skip) {
+ if (walker->fn(tp,(unsigned long) &f->result,
+ walker) < 0) {
+ walker->stop = 1;
+ return;
+ }
+ }
+ walker->count++;
+ }
+ }
+}
+
+
+static int tcindex_destroy_element(struct tcf_proto *tp,
+ unsigned long arg, struct tcf_walker *walker)
+{
+ return tcindex_delete(tp,arg);
+}
+
+
+static void tcindex_destroy(struct tcf_proto *tp)
+{
+ struct tcindex_data *p = PRIV(tp);
+ struct tcf_walker walker;
+
+ DPRINTK("tcindex_destroy(tp %p),p %p\n",tp,p);
+ walker.count = 0;
+ walker.skip = 0;
+ walker.fn = &tcindex_destroy_element;
+ tcindex_walk(tp,&walker);
+ if (p->perfect)
+ kfree(p->perfect);
+ if (p->h)
+ kfree(p->h);
+ kfree(p);
+ tp->root = NULL;
+ MOD_DEC_USE_COUNT;
+}
+
+
+#ifdef CONFIG_RTNETLINK
+
+static int tcindex_dump(struct tcf_proto *tp, unsigned long fh,
+ struct sk_buff *skb, struct tcmsg *t)
+{
+ struct tcindex_data *p = PRIV(tp);
+ struct tcindex_filter_result *r = (struct tcindex_filter_result *) fh;
+ unsigned char *b = skb->tail;
+ struct rtattr *rta;
+
+ DPRINTK("tcindex_dump(tp %p,fh 0x%lx,skb %p,t %p),p %p,r %p,b %p\n",
+ tp,fh,skb,t,p,r,b);
+ DPRINTK("p->perfect %p p->h %p\n",p->perfect,p->h);
+ rta = (struct rtattr *) b;
+ RTA_PUT(skb,TCA_OPTIONS,0,NULL);
+ if (!fh) {
+ t->tcm_handle = ~0; /* whatever ... */
+ RTA_PUT(skb,TCA_TCINDEX_HASH,sizeof(p->hash),&p->hash);
+ RTA_PUT(skb,TCA_TCINDEX_MASK,sizeof(p->mask),&p->mask);
+ RTA_PUT(skb,TCA_TCINDEX_SHIFT,sizeof(p->shift),&p->shift);
+ RTA_PUT(skb,TCA_TCINDEX_FALL_THROUGH,sizeof(p->fall_through),
+ &p->fall_through);
+ } else {
+ if (p->perfect) {
+ t->tcm_handle = r-p->perfect;
+ } else {
+ struct tcindex_filter *f;
+ int i;
+
+ t->tcm_handle = 0;
+ for (i = 0; !t->tcm_handle && i < p->hash; i++) {
+ for (f = p->h[i]; !t->tcm_handle && f;
+ f = f->next) {
+ if (&f->result == r)
+ t->tcm_handle = f->key;
+ }
+ }
+ }
+ DPRINTK("handle = %d\n",t->tcm_handle);
+ if (r->res.class)
+ RTA_PUT(skb, TCA_TCINDEX_CLASSID, 4, &r->res.classid);
+#ifdef CONFIG_NET_CLS_POLICE
+ if (r->police) {
+ struct rtattr *p_rta = (struct rtattr *) skb->tail;
+
+ RTA_PUT(skb,TCA_TCINDEX_POLICE,0,NULL);
+ if (tcf_police_dump(skb,r->police) < 0)
+ goto rtattr_failure;
+ p_rta->rta_len = skb->tail-(u8 *) p_rta;
+ }
+#endif
+ }
+ rta->rta_len = skb->tail-b;
+ return skb->len;
+
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+#endif
+
+
+struct tcf_proto_ops cls_tcindex_ops = {
+ NULL,
+ "tcindex",
+ tcindex_classify,
+ tcindex_init,
+ tcindex_destroy,
+
+ tcindex_get,
+ tcindex_put,
+ tcindex_change,
+ tcindex_delete,
+ tcindex_walk,
+#ifdef CONFIG_RTNETLINK
+ tcindex_dump
+#else
+ NULL
+#endif
+};
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ return register_tcf_proto_ops(&cls_tcindex_ops);
+}
+
+void cleanup_module(void)
+{
+ unregister_tcf_proto_ops(&cls_tcindex_ops);
+}
+#endif
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 09f16a51c..1d57af985 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -12,6 +12,7 @@
*
* Rani Assaf <rani@magic.metawire.com> :980802: JIFFIES and CPU clock sources are repaired.
* Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support
+ * Jamal Hadi Salim <hadi@nortelnetworks.com>: 990601: ingress support
*/
#include <linux/config.h>
@@ -210,6 +211,7 @@ struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid)
if (cops == NULL)
return NULL;
cl = cops->get(p, classid);
+
if (cl == 0)
return NULL;
leaf = cops->leaf(p, cl);
@@ -306,17 +308,32 @@ dev_graft_qdisc(struct net_device *dev, struct Qdisc *qdisc)
write_lock(&qdisc_tree_lock);
spin_lock_bh(&dev->queue_lock);
- oqdisc = dev->qdisc_sleeping;
+ if (qdisc && qdisc->flags&TCQ_F_INGRES) {
+ oqdisc = dev->qdisc_ingress;
+ /* Prune old scheduler */
+ if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1) {
+ /* delete */
+ qdisc_reset(oqdisc);
+ dev->qdisc_ingress = NULL;
+ } else { /* new */
+ dev->qdisc_ingress = qdisc;
+ }
+
+ } else {
+
+ oqdisc = dev->qdisc_sleeping;
- /* Prune old scheduler */
- if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1)
- qdisc_reset(oqdisc);
+ /* Prune old scheduler */
+ if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1)
+ qdisc_reset(oqdisc);
+
+ /* ... and graft new one */
+ if (qdisc == NULL)
+ qdisc = &noop_qdisc;
+ dev->qdisc_sleeping = qdisc;
+ dev->qdisc = &noop_qdisc;
+ }
- /* ... and graft new one */
- if (qdisc == NULL)
- qdisc = &noop_qdisc;
- dev->qdisc_sleeping = qdisc;
- dev->qdisc = &noop_qdisc;
spin_unlock_bh(&dev->queue_lock);
write_unlock(&qdisc_tree_lock);
@@ -337,9 +354,15 @@ int qdisc_graft(struct net_device *dev, struct Qdisc *parent, u32 classid,
struct Qdisc *new, struct Qdisc **old)
{
int err = 0;
+ struct Qdisc *q = *old;
- if (parent == NULL) {
- *old = dev_graft_qdisc(dev, new);
+
+ if (parent == NULL) {
+ if (q && q->flags&TCQ_F_INGRES) {
+ *old = dev_graft_qdisc(dev, q);
+ } else {
+ *old = dev_graft_qdisc(dev, new);
+ }
} else {
struct Qdisc_class_ops *cops = parent->ops->cl_ops;
@@ -406,6 +429,10 @@ qdisc_create(struct net_device *dev, u32 handle, struct rtattr **tca, int *errp)
memset(sch, 0, size);
skb_queue_head_init(&sch->q);
+
+ if (handle == TC_H_INGRESS)
+ sch->flags |= TCQ_F_INGRES;
+
sch->ops = ops;
sch->enqueue = ops->enqueue;
sch->dequeue = ops->dequeue;
@@ -418,7 +445,11 @@ qdisc_create(struct net_device *dev, u32 handle, struct rtattr **tca, int *errp)
if (handle == 0)
goto err_out;
}
- sch->handle = handle;
+
+ if (handle == TC_H_INGRESS)
+ sch->handle =TC_H_MAKE(TC_H_INGRESS, 0);
+ else
+ sch->handle = handle;
if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS-1])) == 0) {
write_lock(&qdisc_tree_lock);
@@ -518,12 +549,16 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
if (clid) {
if (clid != TC_H_ROOT) {
- if ((p = qdisc_lookup(dev, TC_H_MAJ(clid))) == NULL)
- return -ENOENT;
- q = qdisc_leaf(p, clid);
- } else
+ if (TC_H_MAJ(clid) != TC_H_MAJ(TC_H_INGRESS)) {
+ if ((p = qdisc_lookup(dev, TC_H_MAJ(clid))) == NULL)
+ return -ENOENT;
+ q = qdisc_leaf(p, clid);
+ } else { /* ingress */
+ q = dev->qdisc_ingress;
+ }
+ } else {
q = dev->qdisc_sleeping;
-
+ }
if (!q)
return -ENOENT;
@@ -575,9 +610,13 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
if (clid) {
if (clid != TC_H_ROOT) {
- if ((p = qdisc_lookup(dev, TC_H_MAJ(clid))) == NULL)
- return -ENOENT;
- q = qdisc_leaf(p, clid);
+ if (clid != TC_H_INGRESS) {
+ if ((p = qdisc_lookup(dev, TC_H_MAJ(clid))) == NULL)
+ return -ENOENT;
+ q = qdisc_leaf(p, clid);
+ } else { /*ingress */
+ q = dev->qdisc_ingress;
+ }
} else {
q = dev->qdisc_sleeping;
}
@@ -655,7 +694,10 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
create_n_graft:
if (!(n->nlmsg_flags&NLM_F_CREATE))
return -ENOENT;
- q = qdisc_create(dev, tcm->tcm_handle, tca, &err);
+ if (clid == TC_H_INGRESS)
+ q = qdisc_create(dev, tcm->tcm_parent, tca, &err);
+ else
+ q = qdisc_create(dev, tcm->tcm_handle, tca, &err);
if (q == NULL)
return err;
@@ -1190,6 +1232,9 @@ int __init pktsched_init(void)
#ifdef CONFIG_NET_SCH_GRED
INIT_QDISC(gred);
#endif
+#ifdef CONFIG_NET_SCH_INGRESS
+ INIT_QDISC(ingress);
+#endif
#ifdef CONFIG_NET_SCH_DSMARK
INIT_QDISC(dsmark);
#endif
diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c
new file mode 100644
index 000000000..bb98653e1
--- /dev/null
+++ b/net/sched/sch_dsmark.c
@@ -0,0 +1,476 @@
+/* net/sched/sch_dsmark.c - Differentiated Services field marker */
+
+/* Written 1998,1999 by Werner Almesberger, EPFL ICA */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h> /* for pkt_sched */
+#include <linux/rtnetlink.h>
+#include <net/pkt_sched.h>
+#include <net/dsfield.h>
+#include <asm/byteorder.h>
+
+
+#if 1 /* control */
+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define DPRINTK(format,args...)
+#endif
+
+#if 0 /* data */
+#define D2PRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define D2PRINTK(format,args...)
+#endif
+
+
+#define PRIV(sch) ((struct dsmark_qdisc_data *) (sch)->data)
+
+
+/*
+ * classid class marking
+ * ------- ----- -------
+ * n/a 0 n/a
+ * x:0 1 use entry [0]
+ * ... ... ...
+ * x:y y>0 y+1 use entry [y]
+ * ... ... ...
+ * x:indices-1 indices use entry [indices-1]
+ */
+
+
+struct dsmark_qdisc_data {
+ struct Qdisc *q;
+ struct tcf_proto *filter_list;
+ __u8 *mask; /* "owns" the array */
+ __u8 *value;
+ __u16 indices;
+ __u16 default_index;
+ int set_tc_index;
+};
+
+
+/* ------------------------- Class/flow operations ------------------------- */
+
+
+static int dsmark_graft(struct Qdisc *sch,unsigned long arg,
+ struct Qdisc *new,struct Qdisc **old)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+
+ DPRINTK("dsmark_graft(sch %p,[qdisc %p],new %p,old %p)\n",sch,p,new,
+ old);
+ if (!new)
+ new = &noop_qdisc;
+ sch_tree_lock(sch);
+ *old = xchg(&p->q,new);
+ if (*old)
+ qdisc_reset(*old);
+ sch_tree_unlock(sch); /* @@@ move up ? */
+ return 0;
+}
+
+
+static struct Qdisc *dsmark_leaf(struct Qdisc *sch, unsigned long arg)
+{
+ return NULL;
+}
+
+
+static unsigned long dsmark_get(struct Qdisc *sch,u32 classid)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+
+ DPRINTK("dsmark_get(sch %p,[qdisc %p],classid %x)\n",sch,p,classid);
+ return TC_H_MIN(classid)+1;
+}
+
+
+static unsigned long dsmark_bind_filter(struct Qdisc *sch,
+ unsigned long parent, u32 classid)
+{
+ return dsmark_get(sch,classid);
+}
+
+
+static void dsmark_put(struct Qdisc *sch, unsigned long cl)
+{
+}
+
+
+static int dsmark_change(struct Qdisc *sch, u32 classid, u32 parent,
+ struct rtattr **tca, unsigned long *arg)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+ struct rtattr *opt = tca[TCA_OPTIONS-1];
+ struct rtattr *tb[TCA_DSMARK_MAX];
+
+ DPRINTK("dsmark_change(sch %p,[qdisc %p],classid %x,parent %x),"
+ "arg 0x%lx\n",sch,p,classid,parent,*arg);
+ if (*arg > p->indices)
+ return -ENOENT;
+ if (!opt || rtattr_parse(tb, TCA_DSMARK_MAX, RTA_DATA(opt),
+ RTA_PAYLOAD(opt)))
+ return -EINVAL;
+ if (tb[TCA_DSMARK_MASK-1]) {
+ if (!RTA_PAYLOAD(tb[TCA_DSMARK_MASK-1]))
+ return -EINVAL;
+ p->mask[*arg-1] = *(__u8 *) RTA_DATA(tb[TCA_DSMARK_MASK-1]);
+ }
+ if (tb[TCA_DSMARK_VALUE-1]) {
+ if (!RTA_PAYLOAD(tb[TCA_DSMARK_VALUE-1]))
+ return -EINVAL;
+ p->value[*arg-1] = *(__u8 *) RTA_DATA(tb[TCA_DSMARK_VALUE-1]);
+ }
+ return 0;
+}
+
+
+static int dsmark_delete(struct Qdisc *sch,unsigned long arg)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+
+ if (!arg || arg > p->indices)
+ return -EINVAL;
+ p->mask[arg-1] = 0xff;
+ p->value[arg-1] = 0;
+ return 0;
+}
+
+
+static void dsmark_walk(struct Qdisc *sch,struct qdisc_walker *walker)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+ int i;
+
+ DPRINTK("dsmark_walk(sch %p,[qdisc %p],walker %p)\n",sch,p,walker);
+ if (walker->stop)
+ return;
+ for (i = 0; i < p->indices; i++) {
+ if (p->mask[i] == 0xff && !p->value[i])
+ continue;
+ if (walker->count >= walker->skip) {
+ if (walker->fn(sch, i+1, walker) < 0) {
+ walker->stop = 1;
+ break;
+ }
+ }
+ walker->count++;
+ }
+}
+
+
+static struct tcf_proto **dsmark_find_tcf(struct Qdisc *sch,unsigned long cl)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+
+ return &p->filter_list;
+}
+
+
+/* --------------------------- Qdisc operations ---------------------------- */
+
+
+static int dsmark_enqueue(struct sk_buff *skb,struct Qdisc *sch)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+ struct tcf_result res;
+ int result;
+ int ret;
+
+ D2PRINTK("dsmark_enqueue(skb %p,sch %p,[qdisc %p])\n",skb,sch,p);
+ if (p->set_tc_index) {
+ switch (skb->protocol) {
+ case __constant_htons(ETH_P_IP):
+ skb->tc_index = ipv4_get_dsfield(skb->nh.iph);
+ break;
+ case __constant_htons(ETH_P_IPV6):
+ skb->tc_index = ipv6_get_dsfield(skb->nh.ipv6h);
+ break;
+ default:
+ skb->tc_index = 0;
+ break;
+ };
+ }
+ result = TC_POLICE_OK; /* be nice to gcc */
+ if (TC_H_MAJ(skb->priority) == sch->handle) {
+ skb->tc_index = TC_H_MIN(skb->priority);
+ } else {
+ result = tc_classify(skb,p->filter_list,&res);
+ D2PRINTK("result %d class 0x%04x\n",result,res.classid);
+ switch (result) {
+#ifdef CONFIG_NET_CLS_POLICE
+ case TC_POLICE_SHOT:
+ kfree_skb(skb);
+ break;
+#if 0
+ case TC_POLICE_RECLASSIFY:
+ /* FIXME: what to do here ??? */
+#endif
+#endif
+ case TC_POLICE_OK:
+ skb->tc_index = TC_H_MIN(res.classid);
+ break;
+ case TC_POLICE_UNSPEC:
+ /* fall through */
+ default:
+ if (p->default_index)
+ skb->tc_index = p->default_index;
+ break;
+ };
+ }
+ if (
+#ifdef CONFIG_NET_CLS_POLICE
+ result == TC_POLICE_SHOT ||
+#endif
+
+ ((ret = p->q->enqueue(skb,p->q)) != 0)) {
+ sch->stats.drops++;
+ return 0;
+ }
+ sch->stats.bytes += skb->len;
+ sch->stats.packets++;
+ sch->q.qlen++;
+ return ret;
+}
+
+
+static struct sk_buff *dsmark_dequeue(struct Qdisc *sch)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+ struct sk_buff *skb;
+ int index;
+
+ D2PRINTK("dsmark_dequeue(sch %p,[qdisc %p])\n",sch,p);
+ skb = p->q->ops->dequeue(p->q);
+ if (!skb)
+ return NULL;
+ sch->q.qlen--;
+ index = skb->tc_index & (p->indices-1);
+ D2PRINTK("index %d->%d\n",skb->tc_index,index);
+ switch (skb->protocol) {
+ case __constant_htons(ETH_P_IP):
+ ipv4_change_dsfield(skb->nh.iph,
+ p->mask[index],p->value[index]);
+ break;
+ case __constant_htons(ETH_P_IPV6):
+ ipv6_change_dsfield(skb->nh.ipv6h,
+ p->mask[index],p->value[index]);
+ break;
+ default:
+ /*
+ * Only complain if a change was actually attempted.
+ * This way, we can send non-IP traffic through dsmark
+ * and don't need yet another qdisc as a bypass.
+ */
+ if (p->mask[index] != 0xff || p->value[index])
+ printk(KERN_WARNING "dsmark_dequeue: "
+ "unsupported protocol %d\n",
+ htons(skb->protocol));
+ break;
+ };
+ return skb;
+}
+
+
+static int dsmark_requeue(struct sk_buff *skb,struct Qdisc *sch)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+
+ D2PRINTK("dsmark_requeue(skb %p,sch %p,[qdisc %p])\n",skb,sch,p);
+ return p->q->ops->requeue(skb,p->q);
+}
+
+
+static int dsmark_drop(struct Qdisc *sch)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+
+ DPRINTK("dsmark_reset(sch %p,[qdisc %p])\n",sch,p);
+ if (!p->q->ops->drop)
+ return 0;
+ if (!p->q->ops->drop(p->q))
+ return 0;
+ sch->q.qlen--;
+ return 1;
+}
+
+
+int dsmark_init(struct Qdisc *sch,struct rtattr *opt)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+ struct rtattr *tb[TCA_DSMARK_MAX];
+ __u16 tmp;
+
+ DPRINTK("dsmark_init(sch %p,[qdisc %p],opt %p)\n",sch,p,opt);
+ if (rtattr_parse(tb,TCA_DSMARK_MAX,RTA_DATA(opt),RTA_PAYLOAD(opt)) < 0 ||
+ !tb[TCA_DSMARK_INDICES-1] ||
+ RTA_PAYLOAD(tb[TCA_DSMARK_INDICES-1]) < sizeof(__u16))
+ return -EINVAL;
+ memset(p,0,sizeof(*p));
+ p->filter_list = NULL;
+ p->indices = *(__u16 *) RTA_DATA(tb[TCA_DSMARK_INDICES-1]);
+ if (!p->indices)
+ return -EINVAL;
+ for (tmp = p->indices; tmp != 1; tmp >>= 1) {
+ if (tmp & 1)
+ return -EINVAL;
+ }
+ p->default_index = 0;
+ if (tb[TCA_DSMARK_DEFAULT_INDEX-1]) {
+ if (RTA_PAYLOAD(tb[TCA_DSMARK_DEFAULT_INDEX-1]) < sizeof(__u16))
+ return -EINVAL;
+ p->default_index =
+ *(__u16 *) RTA_DATA(tb[TCA_DSMARK_DEFAULT_INDEX-1]);
+ if (!p->default_index || p->default_index >= p->indices)
+ return -EINVAL;
+ }
+ p->set_tc_index = !!tb[TCA_DSMARK_SET_TC_INDEX-1];
+ p->mask = kmalloc(p->indices*2,GFP_KERNEL);
+ if (!p->mask)
+ return -ENOMEM;
+ p->value = p->mask+p->indices;
+ memset(p->mask,0xff,p->indices);
+ memset(p->value,0,p->indices);
+ if (!(p->q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops)))
+ p->q = &noop_qdisc;
+ DPRINTK("dsmark_init: qdisc %p\n",&p->q);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+
+static void dsmark_reset(struct Qdisc *sch)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+
+ DPRINTK("dsmark_reset(sch %p,[qdisc %p])\n",sch,p);
+ qdisc_reset(p->q);
+ sch->q.qlen = 0;
+}
+
+
+static void dsmark_destroy(struct Qdisc *sch)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+ struct tcf_proto *tp;
+
+ DPRINTK("dsmark_destroy(sch %p,[qdisc %p])\n",sch,p);
+ while (p->filter_list) {
+ tp = p->filter_list;
+ p->filter_list = tp->next;
+ tp->ops->destroy(tp);
+ }
+ qdisc_destroy(p->q);
+ p->q = &noop_qdisc;
+ kfree(p->mask);
+ MOD_DEC_USE_COUNT;
+}
+
+
+#ifdef CONFIG_RTNETLINK
+
+static int dsmark_dump_class(struct Qdisc *sch, unsigned long cl,
+ struct sk_buff *skb, struct tcmsg *tcm)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+ unsigned char *b = skb->tail;
+ struct rtattr *rta;
+
+ DPRINTK("dsmark_dump_class(sch %p,[qdisc %p],class %ld\n",sch,p,cl);
+ if (!cl || cl > p->indices)
+ return -EINVAL;
+ tcm->tcm_handle = TC_H_MAKE(TC_H_MAJ(sch->handle),cl-1);
+ rta = (struct rtattr *) b;
+ RTA_PUT(skb,TCA_OPTIONS,0,NULL);
+ RTA_PUT(skb,TCA_DSMARK_MASK,1,&p->mask[cl-1]);
+ RTA_PUT(skb,TCA_DSMARK_VALUE,1,&p->value[cl-1]);
+ rta->rta_len = skb->tail-b;
+ return skb->len;
+
+rtattr_failure:
+ skb_trim(skb,b-skb->data);
+ return -1;
+}
+
+static int dsmark_dump(struct Qdisc *sch, struct sk_buff *skb)
+{
+ struct dsmark_qdisc_data *p = PRIV(sch);
+ unsigned char *b = skb->tail;
+ struct rtattr *rta;
+
+ rta = (struct rtattr *) b;
+ RTA_PUT(skb,TCA_OPTIONS,0,NULL);
+ RTA_PUT(skb,TCA_DSMARK_INDICES,sizeof(__u16),&p->indices);
+ if (p->default_index)
+ RTA_PUT(skb,TCA_DSMARK_DEFAULT_INDEX, sizeof(__u16),
+ &p->default_index);
+ if (p->set_tc_index)
+ RTA_PUT(skb, TCA_DSMARK_SET_TC_INDEX, 0, NULL);
+ rta->rta_len = skb->tail-b;
+ return skb->len;
+
+rtattr_failure:
+ skb_trim(skb,b-skb->data);
+ return -1;
+}
+
+#endif
+
+
+static struct Qdisc_class_ops dsmark_class_ops =
+{
+ dsmark_graft, /* graft */
+ dsmark_leaf, /* leaf */
+ dsmark_get, /* get */
+ dsmark_put, /* put */
+ dsmark_change, /* change */
+ dsmark_delete, /* delete */
+ dsmark_walk, /* walk */
+
+ dsmark_find_tcf, /* tcf_chain */
+ dsmark_bind_filter, /* bind_tcf */
+ dsmark_put, /* unbind_tcf */
+
+#ifdef CONFIG_RTNETLINK
+ dsmark_dump_class, /* dump */
+#endif
+};
+
+struct Qdisc_ops dsmark_qdisc_ops =
+{
+ NULL, /* next */
+ &dsmark_class_ops, /* cl_ops */
+ "dsmark",
+ sizeof(struct dsmark_qdisc_data),
+
+ dsmark_enqueue, /* enqueue */
+ dsmark_dequeue, /* dequeue */
+ dsmark_requeue, /* requeue */
+ dsmark_drop, /* drop */
+
+ dsmark_init, /* init */
+ dsmark_reset, /* reset */
+ dsmark_destroy, /* destroy */
+ NULL, /* change */
+
+#ifdef CONFIG_RTNETLINK
+ dsmark_dump /* dump */
+#endif
+};
+
+#ifdef MODULE
+int init_module(void)
+{
+ return register_qdisc(&dsmark_qdisc_ops);
+}
+
+
+void cleanup_module(void)
+{
+ unregister_qdisc(&dsmark_qdisc_ops);
+}
+#endif
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 2faf6094f..65e4c3e36 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -7,6 +7,8 @@
* 2 of the License, or (at your option) any later version.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * Jamal Hadi Salim, <hadi@nortelnetworks.com> 990601
+ * - Ingress support
*/
#include <asm/uaccess.h>
@@ -590,6 +592,12 @@ void dev_shutdown(struct net_device *dev)
dev->qdisc = &noop_qdisc;
dev->qdisc_sleeping = &noop_qdisc;
qdisc_destroy(qdisc);
+#ifdef CONFIG_NET_SCH_INGRESS
+ if ((qdisc = dev->qdisc_ingress) != NULL) {
+ dev->qdisc_ingress = NULL;
+ qdisc_destroy(qdisc);
+ }
+#endif
BUG_TRAP(dev->qdisc_list == NULL);
dev->qdisc_list = NULL;
spin_unlock_bh(&dev->queue_lock);
diff --git a/net/sched/sch_gred.c b/net/sched/sch_gred.c
new file mode 100644
index 000000000..a9f05e20f
--- /dev/null
+++ b/net/sched/sch_gred.c
@@ -0,0 +1,606 @@
+/*
+ * net/sched/sch_gred.c Generic Random Early Detection queue.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: J Hadi Salim (hadi@nortelnetworks.com) 1998,1999
+ *
+ * 991129: - Bug fix with grio mode
+ * - a better sing. AvgQ mode with Grio
+ * - A finer grained VQ dequeue based on sugestion
+ * from Ren Liu
+ *
+ *
+ *
+ * For all the glorious comments look at Alexey's sch_red.c
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/notifier.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/pkt_sched.h>
+
+#if 1 /* control */
+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define DPRINTK(format,args...)
+#endif
+
+#if 0 /* data */
+#define D2PRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define D2PRINTK(format,args...)
+#endif
+
+struct gred_sched_data;
+struct gred_sched;
+
+struct gred_sched_data
+{
+/* Parameters */
+ u32 limit; /* HARD maximal queue length */
+ u32 qth_min; /* Min average length threshold: A scaled */
+ u32 qth_max; /* Max average length threshold: A scaled */
+ u32 DP; /* the drop pramaters */
+ char Wlog; /* log(W) */
+ char Plog; /* random number bits */
+ u32 Scell_max;
+ u32 Rmask;
+ u32 bytesin; /* bytes seen on virtualQ so far*/
+ u32 packetsin; /* packets seen on virtualQ so far*/
+ u32 backlog; /* bytes on the virtualQ */
+ u32 forced; /* packets dropped for exceeding limits */
+ u32 early; /* packets dropped as a warning */
+ u32 other; /* packets dropped by invoking drop() */
+ u32 pdrop; /* packets dropped because we exceeded physical queue limits */
+ char Scell_log;
+ u8 Stab[256];
+ u8 prio; /* the prio of this vq */
+
+/* Variables */
+ unsigned long qave; /* Average queue length: A scaled */
+ int qcount; /* Packets since last random number generation */
+ u32 qR; /* Cached random number */
+
+ psched_time_t qidlestart; /* Start of idle period */
+};
+
+struct gred_sched
+{
+ struct gred_sched_data *tab[MAX_DPs];
+ u32 DPs;
+ u32 def;
+ u8 initd;
+ u8 grio;
+ u8 eqp;
+};
+
+static int
+gred_enqueue(struct sk_buff *skb, struct Qdisc* sch)
+{
+ psched_time_t now;
+ struct gred_sched_data *q=NULL;
+ struct gred_sched *t= (struct gred_sched *)sch->data;
+ unsigned long qave=0;
+ int i=0;
+
+ if (!t->initd) {
+ DPRINTK("NO GRED Queues setup yet! Enqueued anyway\n");
+ if (q->backlog <= q->limit) {
+ __skb_queue_tail(&sch->q, skb);
+ return NET_XMIT_DROP; /* @@@@ */
+ }
+ }
+
+
+ if ( ((skb->tc_index&0xf) > t->DPs) || !(q=t->tab[skb->tc_index&0xf])) {
+ printk("GRED: setting to default (%d)\n ",t->def);
+ if (!(q=t->tab[t->def])) {
+ DPRINTK("GRED: setting to default FAILED! dropping!! "
+ "(%d)\n ", t->def);
+ goto drop;
+ }
+ /* fix tc_index? --could be controvesial but needed for
+ requeueing */
+ skb->tc_index=(skb->tc_index&0xfffffff0) | t->def;
+ }
+
+ D2PRINTK("gred_enqueue virtualQ 0x%x classid %x backlog %d "
+ "general backlog %d\n",skb->tc_index&0xf,sch->handle,q->backlog,
+ sch->stats.backlog);
+ /* sum up all the qaves of prios <= to ours to get the new qave*/
+ if (t->grio) {
+ for (i=0;i<t->DPs;i++) {
+ if ((!t->tab[i]) || (i==q->DP))
+ continue;
+ if (t->tab[i]->prio == q->prio ){
+ qave=0;
+ t->eqp=1;
+ q->qave=t->tab[t->def]->qave;
+ q->qidlestart=t->tab[t->def]->qidlestart;
+ break;
+ }
+
+ if ((t->tab[i]->prio < q->prio) && (PSCHED_IS_PASTPERFECT(t->tab[i]->qidlestart)))
+ qave +=t->tab[i]->qave;
+ }
+
+ }
+
+ q->packetsin++;
+ q->bytesin+=skb->len;
+
+ if (!PSCHED_IS_PASTPERFECT(q->qidlestart)) {
+ long us_idle;
+ PSCHED_GET_TIME(now);
+ us_idle = PSCHED_TDIFF_SAFE(now, q->qidlestart, q->Scell_max, 0);
+ PSCHED_SET_PASTPERFECT(q->qidlestart);
+
+ q->qave >>= q->Stab[(us_idle>>q->Scell_log)&0xFF];
+ } else {
+ q->qave += q->backlog - (q->qave >> q->Wlog);
+ }
+
+
+ if (t->eqp && t->grio)
+ t->tab[t->def]->qave=q->qave;
+
+ if ((q->qave+qave) < q->qth_min) {
+ q->qcount = -1;
+enqueue:
+ if (q->backlog <= q->limit) {
+ __skb_queue_tail(&sch->q, skb);
+ sch->stats.backlog += skb->len;
+ sch->stats.bytes += skb->len;
+ sch->stats.packets++;
+ q->backlog += skb->len;
+ return 0;
+ } else {
+ q->pdrop++;
+ }
+
+drop:
+ kfree_skb(skb);
+ sch->stats.drops++;
+ return NET_XMIT_DROP;
+ }
+ if ((q->qave+qave) >= q->qth_max) {
+ q->qcount = -1;
+ sch->stats.overlimits++;
+ q->forced++;
+ goto drop;
+ }
+ if (++q->qcount) {
+ if ((((qave+q->qave) - q->qth_min)>>q->Wlog)*q->qcount < q->qR)
+ goto enqueue;
+ q->qcount = 0;
+ q->qR = net_random()&q->Rmask;
+ sch->stats.overlimits++;
+ q->early++;
+ goto drop;
+ }
+ q->qR = net_random()&q->Rmask;
+ goto enqueue;
+}
+
+static int
+gred_requeue(struct sk_buff *skb, struct Qdisc* sch)
+{
+ struct gred_sched_data *q;
+ struct gred_sched *t= (struct gred_sched *)sch->data;
+ q= t->tab[(skb->tc_index&0xf)];
+/* error checking here -- probably unnecessary */
+ PSCHED_SET_PASTPERFECT(q->qidlestart);
+
+ __skb_queue_head(&sch->q, skb);
+ sch->stats.backlog += skb->len;
+ q->backlog += skb->len;
+ return 0;
+}
+
+static struct sk_buff *
+gred_dequeue(struct Qdisc* sch)
+{
+ struct sk_buff *skb;
+ struct gred_sched_data *q;
+ struct gred_sched *t= (struct gred_sched *)sch->data;
+
+ skb = __skb_dequeue(&sch->q);
+ if (skb) {
+ q= t->tab[(skb->tc_index&0xf)];
+ sch->stats.backlog -= skb->len;
+ q->backlog -= skb->len;
+ if (!q->backlog && !t->eqp)
+ PSCHED_GET_TIME(q->qidlestart);
+ return skb;
+ }
+
+ if (t->eqp) {
+ q= t->tab[t->def];
+ if (!q)
+ printk("no default VQ set: Results will be "
+ "screwed up\n");
+ else
+ PSCHED_GET_TIME(q->qidlestart);
+ }
+
+ return NULL;
+}
+
+static int
+gred_drop(struct Qdisc* sch)
+{
+ struct sk_buff *skb;
+ int i;
+
+ struct gred_sched_data *q;
+ struct gred_sched *t= (struct gred_sched *)sch->data;
+
+ skb = __skb_dequeue_tail(&sch->q);
+ if (skb) {
+ q= t->tab[(skb->tc_index&0xf)];
+ sch->stats.backlog -= skb->len;
+ sch->stats.drops++;
+ q->backlog -= skb->len;
+ q->other++;
+ kfree_skb(skb);
+ return 1;
+ }
+
+/* could probably do it for a single VQ before freeing the skb */
+ for (i=0;i<t->DPs;i++) {
+ q= t->tab[i];
+ if (!q)
+ continue;
+ PSCHED_GET_TIME(q->qidlestart);
+ }
+
+ return 0;
+}
+
+static void gred_reset(struct Qdisc* sch)
+{
+ struct sk_buff *skb;
+ int i;
+
+ struct gred_sched_data *q;
+ struct gred_sched *t= (struct gred_sched *)sch->data;
+
+ while((skb=__skb_dequeue(&sch->q))!=NULL)
+ kfree_skb(skb);
+ sch->stats.backlog = 0;
+
+/* could probably do it for a single VQ before freeing the skb */
+ for (i=0;i<t->DPs;i++) {
+ q= t->tab[i];
+ if (!q)
+ continue;
+ PSCHED_SET_PASTPERFECT(q->qidlestart);
+ q->qave = 0;
+ q->qcount = -1;
+ q->backlog = 0;
+ q->other=0;
+ q->forced=0;
+ q->pdrop=0;
+ q->early=0;
+ }
+}
+
+static int gred_change(struct Qdisc *sch, struct rtattr *opt)
+{
+ struct gred_sched *table = (struct gred_sched *)sch->data;
+ struct gred_sched_data *q;
+ struct tc_gred_qopt *ctl;
+ struct tc_gred_sopt *sopt;
+ struct rtattr *tb[TCA_GRED_STAB];
+ struct rtattr *tb2[TCA_GRED_STAB];
+
+ if (opt == NULL ||
+ rtattr_parse(tb, TCA_GRED_STAB, RTA_DATA(opt), RTA_PAYLOAD(opt)) )
+ return -EINVAL;
+
+ if (tb[TCA_GRED_PARMS-1] == 0 && tb[TCA_GRED_STAB-1] == 0 &&
+ tb[TCA_GRED_DPS-1] != 0) {
+ rtattr_parse(tb2, TCA_GRED_DPS, RTA_DATA(opt),
+ RTA_PAYLOAD(opt));
+
+ sopt = RTA_DATA(tb2[TCA_GRED_DPS-1]);
+ table->DPs=sopt->DPs;
+ table->def=sopt->def_DP;
+ table->grio=sopt->grio;
+ table->initd=0;
+ /* probably need to clear all the table DP entries as well */
+ MOD_INC_USE_COUNT;
+ return 0;
+ }
+
+
+ if (!table->DPs || tb[TCA_GRED_PARMS-1] == 0 || tb[TCA_GRED_STAB-1] == 0 ||
+ RTA_PAYLOAD(tb[TCA_GRED_PARMS-1]) < sizeof(*ctl) ||
+ RTA_PAYLOAD(tb[TCA_GRED_STAB-1]) < 256)
+ return -EINVAL;
+
+ ctl = RTA_DATA(tb[TCA_GRED_PARMS-1]);
+ if (ctl->DP > MAX_DPs-1 || ctl->DP <0) {
+ /* misbehaving is punished! Put in the default drop probability */
+ DPRINTK("\nGRED: DP %u not in the proper range fixed. New DP "
+ "set to default at %d\n",ctl->DP,table->def);
+ ctl->DP=table->def;
+ }
+
+ if (table->tab[ctl->DP] == NULL) {
+ table->tab[ctl->DP]=kmalloc(sizeof(struct gred_sched_data),
+ GFP_KERNEL);
+ memset(table->tab[ctl->DP], 0, (sizeof(struct gred_sched_data)));
+ }
+ q= table->tab[ctl->DP];
+
+ if (table->grio) {
+ if (ctl->prio <=0) {
+ if (table->def && table->tab[table->def]) {
+ DPRINTK("\nGRED: DP %u does not have a prio setting "
+ "default to %d\n",ctl->DP,
+ table->tab[table->def]->prio);
+ q->prio=table->tab[table->def]->prio;
+ } else {
+ DPRINTK("\nGRED: DP %u does not have a prio setting "
+ "default to 8\n",ctl->DP);
+ q->prio=8;
+ }
+ } else {
+ q->prio=ctl->prio;
+ }
+ } else {
+ q->prio=8;
+ }
+
+
+ q->DP=ctl->DP;
+ q->Wlog = ctl->Wlog;
+ q->Plog = ctl->Plog;
+ q->limit = ctl->limit;
+ q->Scell_log = ctl->Scell_log;
+ q->Rmask = ctl->Plog < 32 ? ((1<<ctl->Plog) - 1) : ~0UL;
+ q->Scell_max = (255<<q->Scell_log);
+ q->qth_min = ctl->qth_min<<ctl->Wlog;
+ q->qth_max = ctl->qth_max<<ctl->Wlog;
+ q->qave=0;
+ q->backlog=0;
+ q->qcount = -1;
+ q->other=0;
+ q->forced=0;
+ q->pdrop=0;
+ q->early=0;
+
+ PSCHED_SET_PASTPERFECT(q->qidlestart);
+ memcpy(q->Stab, RTA_DATA(tb[TCA_GRED_STAB-1]), 256);
+
+ if (!table->initd) {
+ table->initd=1;
+ /*
+ the first entry also goes into the default until
+ over-written
+ */
+
+ if (table->tab[table->def] == NULL) {
+ table->tab[table->def]=
+ kmalloc(sizeof(struct gred_sched_data), GFP_KERNEL);
+ memset(table->tab[table->def], 0,
+ (sizeof(struct gred_sched_data)));
+ }
+ q= table->tab[table->def];
+ q->DP=table->def;
+ q->Wlog = ctl->Wlog;
+ q->Plog = ctl->Plog;
+ q->limit = ctl->limit;
+ q->Scell_log = ctl->Scell_log;
+ q->Rmask = ctl->Plog < 32 ? ((1<<ctl->Plog) - 1) : ~0UL;
+ q->Scell_max = (255<<q->Scell_log);
+ q->qth_min = ctl->qth_min<<ctl->Wlog;
+ q->qth_max = ctl->qth_max<<ctl->Wlog;
+
+ if (table->grio)
+ q->prio=table->tab[ctl->DP]->prio;
+ else
+ q->prio=8;
+
+ q->qcount = -1;
+ PSCHED_SET_PASTPERFECT(q->qidlestart);
+ memcpy(q->Stab, RTA_DATA(tb[TCA_GRED_STAB-1]), 256);
+ }
+ return 0;
+
+}
+
+static int gred_init(struct Qdisc *sch, struct rtattr *opt)
+{
+ struct gred_sched *table = (struct gred_sched *)sch->data;
+ struct tc_gred_sopt *sopt;
+ struct rtattr *tb[TCA_GRED_STAB];
+ struct rtattr *tb2[TCA_GRED_STAB];
+
+ if (opt == NULL ||
+ rtattr_parse(tb, TCA_GRED_STAB, RTA_DATA(opt), RTA_PAYLOAD(opt)) )
+ return -EINVAL;
+
+ if (tb[TCA_GRED_PARMS-1] == 0 && tb[TCA_GRED_STAB-1] == 0 &&
+ tb[TCA_GRED_DPS-1] != 0) {
+ rtattr_parse(tb2, TCA_GRED_DPS, RTA_DATA(opt),
+ RTA_PAYLOAD(opt));
+
+ sopt = RTA_DATA(tb2[TCA_GRED_DPS-1]);
+ table->DPs=sopt->DPs;
+ table->def=sopt->def_DP;
+ table->grio=sopt->grio;
+ table->initd=0;
+ MOD_INC_USE_COUNT;
+ return 0;
+ }
+
+ DPRINTK("\n GRED_INIT error!\n");
+ return -EINVAL;
+}
+
+#ifdef CONFIG_RTNETLINK
+static int gred_dump(struct Qdisc *sch, struct sk_buff *skb)
+{
+ unsigned long qave;
+ struct rtattr *rta;
+ struct tc_gred_qopt *opt;
+ struct tc_gred_qopt *dst;
+ struct gred_sched *table = (struct gred_sched *)sch->data;
+ struct gred_sched_data *q;
+ int i;
+ unsigned char *b = skb->tail;
+
+ rta = (struct rtattr*)b;
+ RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
+
+ opt=kmalloc(sizeof(struct tc_gred_qopt)*MAX_DPs, GFP_KERNEL);
+
+ if (opt == NULL) {
+ DPRINTK("gred_dump:failed to malloc for %d\n",
+ sizeof(struct tc_gred_qopt)*MAX_DPs);
+ goto rtattr_failure;
+ }
+
+ memset(opt, 0, (sizeof(struct tc_gred_qopt))*table->DPs);
+
+ if (!table->initd) {
+ DPRINTK("NO GRED Queues setup!\n");
+ return -1;
+ }
+
+ for (i=0;i<MAX_DPs;i++) {
+ dst= &opt[i];
+ q= table->tab[i];
+
+ if (!q) {
+ /* hack -- fix at some point with proper message
+ This is how we indicate to tc that there is no VQ
+ at this DP */
+
+ dst->DP=MAX_DPs+i;
+ continue;
+ }
+
+ dst->limit=q->limit;
+ dst->qth_min=q->qth_min>>q->Wlog;
+ dst->qth_max=q->qth_max>>q->Wlog;
+ dst->DP=q->DP;
+ dst->backlog=q->backlog;
+ if (q->qave) {
+ if (table->eqp && table->grio) {
+ q->qidlestart=table->tab[table->def]->qidlestart;
+ q->qave=table->tab[table->def]->qave;
+ }
+ if (!PSCHED_IS_PASTPERFECT(q->qidlestart)) {
+ long idle;
+ psched_time_t now;
+ PSCHED_GET_TIME(now);
+ idle = PSCHED_TDIFF_SAFE(now, q->qidlestart, q->Scell_max, 0);
+ qave = q->qave >> q->Stab[(idle>>q->Scell_log)&0xFF];
+ dst->qave = qave >> q->Wlog;
+
+ } else {
+ dst->qave = q->qave >> q->Wlog;
+ }
+ } else {
+ dst->qave = 0;
+ }
+
+
+ dst->Wlog = q->Wlog;
+ dst->Plog = q->Plog;
+ dst->Scell_log = q->Scell_log;
+ dst->other = q->other;
+ dst->forced = q->forced;
+ dst->early = q->early;
+ dst->pdrop = q->pdrop;
+ dst->prio = q->prio;
+ dst->packets=q->packetsin;
+ dst->bytesin=q->bytesin;
+ }
+
+ RTA_PUT(skb, TCA_GRED_PARMS, sizeof(struct tc_gred_qopt)*MAX_DPs, opt);
+ rta->rta_len = skb->tail - b;
+
+ return skb->len;
+
+rtattr_failure:
+ DPRINTK("gred_dump: FAILURE!!!!\n");
+
+/* also free the opt struct here */
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+#endif
+
+static void gred_destroy(struct Qdisc *sch)
+{
+ struct gred_sched *table = (struct gred_sched *)sch->data;
+ int i;
+
+ for (i = 0;i < table->DPs; i++) {
+ if (table->tab[i])
+ kfree(table->tab[i]);
+ }
+ MOD_DEC_USE_COUNT;
+}
+
+struct Qdisc_ops gred_qdisc_ops =
+{
+ NULL,
+ NULL,
+ "gred",
+ sizeof(struct gred_sched),
+ gred_enqueue,
+ gred_dequeue,
+ gred_requeue,
+ gred_drop,
+ gred_init,
+ gred_reset,
+ gred_destroy,
+ gred_change, /* change */
+#ifdef CONFIG_RTNETLINK
+ gred_dump,
+#endif
+};
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ return register_qdisc(&gred_qdisc_ops);
+}
+
+void cleanup_module(void)
+{
+ unregister_qdisc(&gred_qdisc_ops);
+}
+#endif
diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
new file mode 100644
index 000000000..852f56b22
--- /dev/null
+++ b/net/sched/sch_ingress.c
@@ -0,0 +1,392 @@
+/* net/sched/sch_ingress.c - Ingress qdisc
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Jamal Hadi Salim 1999
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter.h>
+#include <net/pkt_sched.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+#include <asm/smp.h>
+#include <linux/kmod.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+
+
+
+
+#if 0 /* control */
+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define DPRINTK(format,args...)
+#endif
+
+#if 0 /* data */
+#define D2PRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define D2PRINTK(format,args...)
+#endif
+
+
+#define PRIV(sch) ((struct ingress_qdisc_data *) (sch)->data)
+
+
+
+struct ingress_qdisc_data {
+ struct Qdisc *q;
+ struct tcf_proto *filter_list;
+};
+
+
+/* ------------------------- Class/flow operations ------------------------- */
+
+
+static int ingress_graft(struct Qdisc *sch,unsigned long arg,
+ struct Qdisc *new,struct Qdisc **old)
+{
+ struct ingress_qdisc_data *p = PRIV(sch);
+
+ DPRINTK("ingress_graft(sch %p,[qdisc %p],new %p,old %p)\n",
+ sch, p, new, old);
+ DPRINTK("\n ingress_graft: You cannot add qdiscs to classes");
+ return 1;
+}
+
+
+static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg)
+{
+ return NULL;
+}
+
+
+static unsigned long ingress_get(struct Qdisc *sch,u32 classid)
+{
+ struct ingress_qdisc_data *p = PRIV(sch);
+
+ DPRINTK("ingress_get(sch %p,[qdisc %p],classid %x)\n", sch, p, classid);
+ return TC_H_MIN(classid) + 1;
+}
+
+
+static unsigned long ingress_bind_filter(struct Qdisc *sch,
+ unsigned long parent, u32 classid)
+{
+ return ingress_get(sch, classid);
+}
+
+
+static void ingress_put(struct Qdisc *sch, unsigned long cl)
+{
+}
+
+
+static int ingress_change(struct Qdisc *sch, u32 classid, u32 parent,
+ struct rtattr **tca, unsigned long *arg)
+{
+ struct ingress_qdisc_data *p = PRIV(sch);
+
+ DPRINTK("ingress_change(sch %p,[qdisc %p],classid %x,parent %x),"
+ "arg 0x%lx\n", sch, p, classid, parent, *arg);
+ DPRINTK("No effect. sch_ingress doesnt maintain classes at the moment");
+ return 0;
+}
+
+
+
+static void ingress_walk(struct Qdisc *sch,struct qdisc_walker *walker)
+{
+ struct ingress_qdisc_data *p = PRIV(sch);
+
+ DPRINTK("ingress_walk(sch %p,[qdisc %p],walker %p)\n", sch, p, walker);
+ DPRINTK("No effect. sch_ingress doesnt maintain classes at the moment");
+}
+
+
+static struct tcf_proto **ingress_find_tcf(struct Qdisc *sch,unsigned long cl)
+{
+ struct ingress_qdisc_data *p = PRIV(sch);
+
+ return &p->filter_list;
+}
+
+
+/* --------------------------- Qdisc operations ---------------------------- */
+
+
+static int ingress_enqueue(struct sk_buff *skb,struct Qdisc *sch)
+{
+ struct ingress_qdisc_data *p = PRIV(sch);
+ struct tcf_result res;
+ int result;
+
+ D2PRINTK("ingress_enqueue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p);
+ result = tc_classify(skb, p->filter_list, &res);
+ D2PRINTK("result %d class 0x%04x\n", result, res.classid);
+ /*
+ * Unlike normal "enqueue" functions, ingress_enqueue returns a
+ * firewall FW_* code.
+ */
+ switch (result) {
+#ifdef CONFIG_NET_CLS_POLICE
+ case TC_POLICE_SHOT:
+ result = NF_DROP;
+ break;
+ case TC_POLICE_RECLASSIFY: /* DSCP remarking here ? */
+ case TC_POLICE_OK:
+ case TC_POLICE_UNSPEC:
+ default:
+ result = NF_ACCEPT;
+ break;
+#endif
+ };
+
+#ifdef CONFIG_NET_CLS_TCINDEX
+ skb->tc_index = TC_H_MIN(res.classid);
+#endif
+ return result;
+}
+
+
+static struct sk_buff *ingress_dequeue(struct Qdisc *sch)
+{
+/*
+ struct ingress_qdisc_data *p = PRIV(sch);
+ D2PRINTK("ingress_dequeue(sch %p,[qdisc %p])\n",sch,PRIV(p));
+*/
+ return NULL;
+}
+
+
+static int ingress_requeue(struct sk_buff *skb,struct Qdisc *sch)
+{
+/*
+ struct ingress_qdisc_data *p = PRIV(sch);
+ D2PRINTK("ingress_requeue(skb %p,sch %p,[qdisc %p])\n",skb,sch,PRIV(p));
+*/
+ return 0;
+}
+
+static int ingress_drop(struct Qdisc *sch)
+{
+ struct ingress_qdisc_data *p = PRIV(sch);
+
+ DPRINTK("ingress_drop(sch %p,[qdisc %p])\n", sch, p);
+ return 0;
+}
+
+static unsigned int
+ing_hook(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *indev,
+ const struct net_device *outdev,
+ int (*okfn)(struct sk_buff *))
+{
+
+ struct Qdisc *q;
+ struct sk_buff *skb = *pskb;
+ struct net_device *dev = skb->dev;
+ int fwres=NF_ACCEPT;
+
+ DPRINTK("ing_hook: skb %s dev=%s len=%u\n",
+ skb->sk ? "(owned)" : "(unowned)",
+ skb->dev ? (*pskb)->dev->name : "(no dev)",
+ skb->len);
+
+/*
+revisit later: Use a private since lock dev->queue_lock is also
+used on the egress (might slow things for an iota)
+*/
+
+ if (dev->qdisc_ingress) {
+ spin_lock(&dev->queue_lock);
+ if ((q = dev->qdisc_ingress) != NULL)
+ fwres = q->enqueue(skb, q);
+ spin_unlock(&dev->queue_lock);
+ }
+
+ return fwres;
+}
+
+
+/* after iptables */
+static struct nf_hook_ops ing_ops =
+{
+ { NULL, NULL},
+ ing_hook,
+ NULL,
+ PF_INET,
+ NF_IP_PRE_ROUTING,
+ 1
+};
+
+int ingress_init(struct Qdisc *sch,struct rtattr *opt)
+{
+ struct ingress_qdisc_data *p = PRIV(sch);
+
+ DPRINTK("ingress_init(sch %p,[qdisc %p],opt %p)\n",sch,p,opt);
+ memset(p, 0, sizeof(*p));
+ p->filter_list = NULL;
+ p->q = &noop_qdisc;
+#ifndef MODULE
+ if (nf_register_hook(&ing_ops) < 0) {
+ printk("Unable to register ingress \n");
+ goto error;
+ }
+#endif
+ DPRINTK("ingress_init: qdisc %p\n", sch);
+ MOD_INC_USE_COUNT;
+ return 0;
+#ifndef MODULE
+error:
+#endif
+ return -EINVAL;
+}
+
+
+static void ingress_reset(struct Qdisc *sch)
+{
+ struct ingress_qdisc_data *p = PRIV(sch);
+
+ DPRINTK("ingress_reset(sch %p,[qdisc %p])\n", sch, p);
+
+/*
+#if 0
+*/
+/* for future use */
+ qdisc_reset(p->q);
+/*
+#endif
+*/
+}
+
+/* ------------------------------------------------------------- */
+
+
+/* ------------------------------------------------------------- */
+
+static void ingress_destroy(struct Qdisc *sch)
+{
+ struct ingress_qdisc_data *p = PRIV(sch);
+ struct tcf_proto *tp;
+
+ DPRINTK("ingress_destroy(sch %p,[qdisc %p])\n", sch, p);
+ while (p->filter_list) {
+ tp = p->filter_list;
+ p->filter_list = tp->next;
+ tp->ops->destroy(tp);
+ }
+ memset(p, 0, sizeof(*p));
+ p->filter_list = NULL;
+
+#if 0
+/* for future use */
+ qdisc_destroy(p->q);
+#endif
+
+#ifndef MODULE
+ nf_unregister_hook(&ing_ops);
+#endif
+
+ MOD_DEC_USE_COUNT;
+}
+
+
+#ifdef CONFIG_RTNETLINK
+
+
+static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb)
+{
+ unsigned char *b = skb->tail;
+ struct rtattr *rta;
+
+ rta = (struct rtattr *) b;
+ RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
+ rta->rta_len = skb->tail - b;
+ return skb->len;
+
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+#endif
+
+
+static struct Qdisc_class_ops ingress_class_ops =
+{
+ ingress_graft, /* graft */
+ ingress_leaf, /* leaf */
+ ingress_get, /* get */
+ ingress_put, /* put */
+ ingress_change, /* change */
+ NULL, /* delete */
+ ingress_walk, /* walk */
+
+ ingress_find_tcf, /* tcf_chain */
+ ingress_bind_filter, /* bind_tcf */
+ ingress_put, /* unbind_tcf */
+
+#ifdef CONFIG_RTNETLINK
+ NULL, /* dump */
+#endif
+};
+
+struct Qdisc_ops ingress_qdisc_ops =
+{
+ NULL, /* next */
+ &ingress_class_ops, /* cl_ops */
+ "ingress",
+ sizeof(struct ingress_qdisc_data),
+
+ ingress_enqueue, /* enqueue */
+ ingress_dequeue, /* dequeue */
+ ingress_requeue, /* requeue */
+ ingress_drop, /* drop */
+
+ ingress_init, /* init */
+ ingress_reset, /* reset */
+ ingress_destroy, /* destroy */
+ NULL, /* change */
+
+#ifdef CONFIG_RTNETLINK
+ ingress_dump, /* dump */
+#endif
+};
+
+#ifdef MODULE
+int init_module(void)
+{
+ int ret = 0;
+
+ if ((ret = register_qdisc(&ingress_qdisc_ops)) < 0) {
+ printk("Unable to register Ingress qdisc\n");
+ return ret;
+ }
+
+ if (nf_register_hook(&ing_ops) < 0) {
+ printk("Unable to register ingress on hook \n");
+ unregister_qdisc(&ingress_qdisc_ops);
+ return 0;
+ }
+
+ return ret;
+}
+
+
+void cleanup_module(void)
+{
+ nf_unregister_hook(&ing_ops);
+ unregister_qdisc(&ingress_qdisc_ops);
+}
+#endif
diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c
index 0fc53b4c9..015cab96b 100644
--- a/net/sched/sch_prio.c
+++ b/net/sched/sch_prio.c
@@ -7,6 +7,8 @@
* 2 of the License, or (at your option) any later version.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * Fixes: 19990609: J Hadi Salim <hadi@nortelnetworks.com>:
+ * Init -- EINVAL when opt undefined
*/
#include <linux/config.h>
@@ -211,8 +213,6 @@ static int prio_tune(struct Qdisc *sch, struct rtattr *opt)
static int prio_init(struct Qdisc *sch, struct rtattr *opt)
{
- static const u8 prio2band[TC_PRIO_MAX+1] =
- { 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 };
struct prio_sched_data *q = (struct prio_sched_data *)sch->data;
int i;
@@ -220,14 +220,7 @@ static int prio_init(struct Qdisc *sch, struct rtattr *opt)
q->queues[i] = &noop_qdisc;
if (opt == NULL) {
- q->bands = 3;
- memcpy(q->prio2band, prio2band, sizeof(prio2band));
- for (i=0; i<3; i++) {
- struct Qdisc *child;
- child = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
- if (child)
- q->queues[i] = child;
- }
+ return -EINVAL;
} else {
int err;
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index 3f666e0f8..ede1e96cd 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -437,7 +437,7 @@ static int teql_master_init(struct net_device *dev)
dev->stop = teql_master_close;
dev->get_stats = teql_master_stats;
dev->change_mtu = teql_master_mtu;
- dev->type = 0;
+ dev->type = ARPHRD_VOID;
dev->mtu = 1500;
dev->tx_queue_len = 100;
dev->flags = IFF_NOARP;