diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-05 06:47:02 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-05 06:47:02 +0000 |
commit | 99a7e12f34b3661a0d1354eef83a0eef4df5e34c (patch) | |
tree | 3560aca9ca86792f9ab7bd87861ea143a1b3c7a3 /net/sched | |
parent | e73a04659c0b8cdee4dd40e58630e2cf63afb316 (diff) |
Merge with Linux 2.3.38.
Diffstat (limited to 'net/sched')
-rw-r--r-- | net/sched/Config.in | 42 | ||||
-rw-r--r-- | net/sched/Makefile | 32 | ||||
-rw-r--r-- | net/sched/cls_api.c | 3 | ||||
-rw-r--r-- | net/sched/cls_tcindex.c | 503 | ||||
-rw-r--r-- | net/sched/sch_api.c | 87 | ||||
-rw-r--r-- | net/sched/sch_dsmark.c | 476 | ||||
-rw-r--r-- | net/sched/sch_generic.c | 8 | ||||
-rw-r--r-- | net/sched/sch_gred.c | 606 | ||||
-rw-r--r-- | net/sched/sch_ingress.c | 392 | ||||
-rw-r--r-- | net/sched/sch_prio.c | 13 | ||||
-rw-r--r-- | net/sched/sch_teql.c | 2 |
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; |