diff options
Diffstat (limited to 'net/sched')
-rw-r--r-- | net/sched/Kconfig | 1 | ||||
-rw-r--r-- | net/sched/act_ipt.c | 10 | ||||
-rw-r--r-- | net/sched/sch_atm.c | 1 | ||||
-rw-r--r-- | net/sched/sch_dsmark.c | 1 | ||||
-rw-r--r-- | net/sched/sch_generic.c | 2 | ||||
-rw-r--r-- | net/sched/sch_netem.c | 4 | ||||
-rw-r--r-- | net/sched/sch_prio.c | 2 | ||||
-rw-r--r-- | net/sched/sch_red.c | 179 | ||||
-rw-r--r-- | net/sched/sch_sfq.c | 5 | ||||
-rw-r--r-- | net/sched/sch_tbf.c | 9 |
10 files changed, 185 insertions, 29 deletions
diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 778b1e5a4b5..13eeee58288 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -434,7 +434,6 @@ config NET_EMATCH_TEXT config NET_CLS_ACT bool "Actions" - depends on EXPERIMENTAL select NET_ESTIMATOR ---help--- Say Y here if you want to use traffic control actions. Actions diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 39a22a3ffe7..6056d20ef42 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -70,7 +70,8 @@ ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int hook) t->u.kernel.target = target; if (t->u.kernel.target->checkentry - && !t->u.kernel.target->checkentry(table, NULL, t->data, + && !t->u.kernel.target->checkentry(table, NULL, + t->u.kernel.target, t->data, t->u.target_size - sizeof(*t), hook)) { DPRINTK("ipt_init_target: check failed for `%s'.\n", @@ -86,7 +87,7 @@ static void ipt_destroy_target(struct ipt_entry_target *t) { if (t->u.kernel.target->destroy) - t->u.kernel.target->destroy(t->data, + t->u.kernel.target->destroy(t->u.kernel.target, t->data, t->u.target_size - sizeof(*t)); module_put(t->u.kernel.target->me); } @@ -224,8 +225,9 @@ tcf_ipt(struct sk_buff *skb, struct tc_action *a, struct tcf_result *res) /* iptables targets take a double skb pointer in case the skb * needs to be replaced. We don't own the skb, so this must not * happen. The pskb_expand_head above should make sure of this */ - ret = p->t->u.kernel.target->target(&skb, skb->dev, NULL, - p->hook, p->t->data, NULL); + ret = p->t->u.kernel.target->target(&skb, skb->dev, NULL, p->hook, + p->t->u.kernel.target, p->t->data, + NULL); switch (ret) { case NF_ACCEPT: result = TC_ACT_OK; diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 93ebce40aca..ac7cb60d1e2 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -638,6 +638,7 @@ static int atm_tc_dump_class(struct Qdisc *sch, unsigned long cl, sch,p,flow,skb,tcm); if (!find_flow(p,flow)) return -EINVAL; tcm->tcm_handle = flow->classid; + tcm->tcm_info = flow->q->handle; rta = (struct rtattr *) b; RTA_PUT(skb,TCA_OPTIONS,0,NULL); RTA_PUT(skb,TCA_ATM_HDR,flow->hdr_len,flow->hdr); diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index 13e0e7b3856..f6320ca7049 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -438,6 +438,7 @@ static int dsmark_dump_class(struct Qdisc *sch, unsigned long cl, return -EINVAL; tcm->tcm_handle = TC_H_MAKE(TC_H_MAJ(sch->handle), cl-1); + tcm->tcm_info = p->q->handle; opts = RTA_NEST(skb, TCA_OPTIONS); RTA_PUT_U8(skb,TCA_DSMARK_MASK, p->mask[cl-1]); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 99ceb91f015..31eb83717c2 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -234,7 +234,7 @@ static void dev_watchdog_down(struct net_device *dev) { spin_lock_bh(&dev->xmit_lock); if (del_timer(&dev->watchdog_timer)) - __dev_put(dev); + dev_put(dev); spin_unlock_bh(&dev->xmit_lock); } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index ba528320483..7228d30512c 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -252,9 +252,9 @@ static int netem_requeue(struct sk_buff *skb, struct Qdisc *sch) static unsigned int netem_drop(struct Qdisc* sch) { struct netem_sched_data *q = qdisc_priv(sch); - unsigned int len; + unsigned int len = 0; - if ((len = q->qdisc->ops->drop(q->qdisc)) != 0) { + if (q->qdisc->ops->drop && (len = q->qdisc->ops->drop(q->qdisc)) != 0) { sch->q.qlen--; sch->qstats.drops++; } diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 1641db33a99..3395ca7bcad 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -165,7 +165,7 @@ static unsigned int prio_drop(struct Qdisc* sch) for (prio = q->bands-1; prio >= 0; prio--) { qdisc = q->queues[prio]; - if ((len = qdisc->ops->drop(qdisc)) != 0) { + if (qdisc->ops->drop && (len = qdisc->ops->drop(qdisc)) != 0) { sch->q.qlen--; return len; } diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index dccfa44c2d7..2be563cba72 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -44,6 +44,7 @@ struct red_sched_data unsigned char flags; struct red_parms parms; struct red_stats stats; + struct Qdisc *qdisc; }; static inline int red_use_ecn(struct red_sched_data *q) @@ -59,8 +60,10 @@ static inline int red_use_harddrop(struct red_sched_data *q) static int red_enqueue(struct sk_buff *skb, struct Qdisc* sch) { struct red_sched_data *q = qdisc_priv(sch); + struct Qdisc *child = q->qdisc; + int ret; - q->parms.qavg = red_calc_qavg(&q->parms, sch->qstats.backlog); + q->parms.qavg = red_calc_qavg(&q->parms, child->qstats.backlog); if (red_is_idling(&q->parms)) red_end_of_idle_period(&q->parms); @@ -91,11 +94,16 @@ static int red_enqueue(struct sk_buff *skb, struct Qdisc* sch) break; } - if (sch->qstats.backlog + skb->len <= q->limit) - return qdisc_enqueue_tail(skb, sch); - - q->stats.pdrop++; - return qdisc_drop(skb, sch); + ret = child->enqueue(skb, child); + if (likely(ret == NET_XMIT_SUCCESS)) { + sch->bstats.bytes += skb->len; + sch->bstats.packets++; + sch->q.qlen++; + } else { + q->stats.pdrop++; + sch->qstats.drops++; + } + return ret; congestion_drop: qdisc_drop(skb, sch); @@ -105,21 +113,30 @@ congestion_drop: static int red_requeue(struct sk_buff *skb, struct Qdisc* sch) { struct red_sched_data *q = qdisc_priv(sch); + struct Qdisc *child = q->qdisc; + int ret; if (red_is_idling(&q->parms)) red_end_of_idle_period(&q->parms); - return qdisc_requeue(skb, sch); + ret = child->ops->requeue(skb, child); + if (likely(ret == NET_XMIT_SUCCESS)) { + sch->qstats.requeues++; + sch->q.qlen++; + } + return ret; } static struct sk_buff * red_dequeue(struct Qdisc* sch) { struct sk_buff *skb; struct red_sched_data *q = qdisc_priv(sch); + struct Qdisc *child = q->qdisc; - skb = qdisc_dequeue_head(sch); - - if (skb == NULL && !red_is_idling(&q->parms)) + skb = child->dequeue(child); + if (skb) + sch->q.qlen--; + else if (!red_is_idling(&q->parms)) red_start_of_idle_period(&q->parms); return skb; @@ -127,14 +144,14 @@ static struct sk_buff * red_dequeue(struct Qdisc* sch) static unsigned int red_drop(struct Qdisc* sch) { - struct sk_buff *skb; struct red_sched_data *q = qdisc_priv(sch); + struct Qdisc *child = q->qdisc; + unsigned int len; - skb = qdisc_dequeue_tail(sch); - if (skb) { - unsigned int len = skb->len; + if (child->ops->drop && (len = child->ops->drop(child)) > 0) { q->stats.other++; - qdisc_drop(skb, sch); + sch->qstats.drops++; + sch->q.qlen--; return len; } @@ -148,15 +165,48 @@ static void red_reset(struct Qdisc* sch) { struct red_sched_data *q = qdisc_priv(sch); - qdisc_reset_queue(sch); + qdisc_reset(q->qdisc); + sch->q.qlen = 0; red_restart(&q->parms); } +static void red_destroy(struct Qdisc *sch) +{ + struct red_sched_data *q = qdisc_priv(sch); + qdisc_destroy(q->qdisc); +} + +static struct Qdisc *red_create_dflt(struct net_device *dev, u32 limit) +{ + struct Qdisc *q = qdisc_create_dflt(dev, &bfifo_qdisc_ops); + struct rtattr *rta; + int ret; + + if (q) { + rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), + GFP_KERNEL); + if (rta) { + rta->rta_type = RTM_NEWQDISC; + rta->rta_len = RTA_LENGTH(sizeof(struct tc_fifo_qopt)); + ((struct tc_fifo_qopt *)RTA_DATA(rta))->limit = limit; + + ret = q->ops->change(q, rta); + kfree(rta); + + if (ret == 0) + return q; + } + qdisc_destroy(q); + } + return NULL; +} + static int red_change(struct Qdisc *sch, struct rtattr *opt) { struct red_sched_data *q = qdisc_priv(sch); struct rtattr *tb[TCA_RED_MAX]; struct tc_red_qopt *ctl; + struct Qdisc *child = NULL; if (opt == NULL || rtattr_parse_nested(tb, TCA_RED_MAX, opt)) return -EINVAL; @@ -169,9 +219,17 @@ static int red_change(struct Qdisc *sch, struct rtattr *opt) ctl = RTA_DATA(tb[TCA_RED_PARMS-1]); + if (ctl->limit > 0) { + child = red_create_dflt(sch->dev, ctl->limit); + if (child == NULL) + return -ENOMEM; + } + sch_tree_lock(sch); q->flags = ctl->flags; q->limit = ctl->limit; + if (child) + qdisc_destroy(xchg(&q->qdisc, child)); red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog, ctl->Plog, ctl->Scell_log, @@ -186,6 +244,9 @@ static int red_change(struct Qdisc *sch, struct rtattr *opt) static int red_init(struct Qdisc* sch, struct rtattr *opt) { + struct red_sched_data *q = qdisc_priv(sch); + + q->qdisc = &noop_qdisc; return red_change(sch, opt); } @@ -224,15 +285,101 @@ static int red_dump_stats(struct Qdisc *sch, struct gnet_dump *d) return gnet_stats_copy_app(d, &st, sizeof(st)); } +static int red_dump_class(struct Qdisc *sch, unsigned long cl, + struct sk_buff *skb, struct tcmsg *tcm) +{ + struct red_sched_data *q = qdisc_priv(sch); + + if (cl != 1) + return -ENOENT; + tcm->tcm_handle |= TC_H_MIN(1); + tcm->tcm_info = q->qdisc->handle; + return 0; +} + +static int red_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, + struct Qdisc **old) +{ + struct red_sched_data *q = qdisc_priv(sch); + + if (new == NULL) + new = &noop_qdisc; + + sch_tree_lock(sch); + *old = xchg(&q->qdisc, new); + qdisc_reset(*old); + sch->q.qlen = 0; + sch_tree_unlock(sch); + return 0; +} + +static struct Qdisc *red_leaf(struct Qdisc *sch, unsigned long arg) +{ + struct red_sched_data *q = qdisc_priv(sch); + return q->qdisc; +} + +static unsigned long red_get(struct Qdisc *sch, u32 classid) +{ + return 1; +} + +static void red_put(struct Qdisc *sch, unsigned long arg) +{ + return; +} + +static int red_change_class(struct Qdisc *sch, u32 classid, u32 parentid, + struct rtattr **tca, unsigned long *arg) +{ + return -ENOSYS; +} + +static int red_delete(struct Qdisc *sch, unsigned long cl) +{ + return -ENOSYS; +} + +static void red_walk(struct Qdisc *sch, struct qdisc_walker *walker) +{ + if (!walker->stop) { + if (walker->count >= walker->skip) + if (walker->fn(sch, 1, walker) < 0) { + walker->stop = 1; + return; + } + walker->count++; + } +} + +static struct tcf_proto **red_find_tcf(struct Qdisc *sch, unsigned long cl) +{ + return NULL; +} + +static struct Qdisc_class_ops red_class_ops = { + .graft = red_graft, + .leaf = red_leaf, + .get = red_get, + .put = red_put, + .change = red_change_class, + .delete = red_delete, + .walk = red_walk, + .tcf_chain = red_find_tcf, + .dump = red_dump_class, +}; + static struct Qdisc_ops red_qdisc_ops = { .id = "red", .priv_size = sizeof(struct red_sched_data), + .cl_ops = &red_class_ops, .enqueue = red_enqueue, .dequeue = red_dequeue, .requeue = red_requeue, .drop = red_drop, .init = red_init, .reset = red_reset, + .destroy = red_destroy, .change = red_change, .dump = red_dump, .dump_stats = red_dump_stats, diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 86d8da0cbd0..e057768f68b 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -232,6 +232,7 @@ static unsigned int sfq_drop(struct Qdisc *sch) sfq_dec(q, x); sch->q.qlen--; sch->qstats.drops++; + sch->qstats.backlog -= len; return len; } @@ -248,6 +249,7 @@ static unsigned int sfq_drop(struct Qdisc *sch) sch->q.qlen--; q->ht[q->hash[d]] = SFQ_DEPTH; sch->qstats.drops++; + sch->qstats.backlog -= len; return len; } @@ -266,6 +268,7 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc* sch) q->ht[hash] = x = q->dep[SFQ_DEPTH].next; q->hash[x] = hash; } + sch->qstats.backlog += skb->len; __skb_queue_tail(&q->qs[x], skb); sfq_inc(q, x); if (q->qs[x].qlen == 1) { /* The flow is new */ @@ -301,6 +304,7 @@ sfq_requeue(struct sk_buff *skb, struct Qdisc* sch) q->ht[hash] = x = q->dep[SFQ_DEPTH].next; q->hash[x] = hash; } + sch->qstats.backlog += skb->len; __skb_queue_head(&q->qs[x], skb); sfq_inc(q, x); if (q->qs[x].qlen == 1) { /* The flow is new */ @@ -344,6 +348,7 @@ sfq_dequeue(struct Qdisc* sch) skb = __skb_dequeue(&q->qs[a]); sfq_dec(q, a); sch->q.qlen--; + sch->qstats.backlog -= skb->len; /* Is the slot empty? */ if (q->qs[a].qlen == 0) { diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index cb9711ea8c6..d8e03c74ca7 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -177,9 +177,9 @@ static int tbf_requeue(struct sk_buff *skb, struct Qdisc* sch) static unsigned int tbf_drop(struct Qdisc* sch) { struct tbf_sched_data *q = qdisc_priv(sch); - unsigned int len; + unsigned int len = 0; - if ((len = q->qdisc->ops->drop(q->qdisc)) != 0) { + if (q->qdisc->ops->drop && (len = q->qdisc->ops->drop(q->qdisc)) != 0) { sch->q.qlen--; sch->qstats.drops++; } @@ -341,13 +341,14 @@ static int tbf_change(struct Qdisc* sch, struct rtattr *opt) if (max_size < 0) goto done; - if (q->qdisc == &noop_qdisc) { + if (qopt->limit > 0) { if ((child = tbf_create_dflt_qdisc(sch->dev, qopt->limit)) == NULL) goto done; } sch_tree_lock(sch); - if (child) q->qdisc = child; + if (child) + qdisc_destroy(xchg(&q->qdisc, child)); q->limit = qopt->limit; q->mtu = qopt->mtu; q->max_size = max_size; |