summaryrefslogtreecommitdiff
path: root/net/sched
diff options
context:
space:
mode:
Diffstat (limited to 'net/sched')
-rw-r--r--net/sched/Kconfig1
-rw-r--r--net/sched/act_ipt.c10
-rw-r--r--net/sched/sch_atm.c1
-rw-r--r--net/sched/sch_dsmark.c1
-rw-r--r--net/sched/sch_generic.c2
-rw-r--r--net/sched/sch_netem.c4
-rw-r--r--net/sched/sch_prio.c2
-rw-r--r--net/sched/sch_red.c179
-rw-r--r--net/sched/sch_sfq.c5
-rw-r--r--net/sched/sch_tbf.c9
10 files changed, 185 insertions, 29 deletions
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 778b1e5a4b50..13eeee582886 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 39a22a3ffe78..6056d20ef429 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 93ebce40acac..ac7cb60d1e25 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 13e0e7b3856b..f6320ca70493 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 99ceb91f0150..31eb83717c26 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 ba5283204837..7228d30512c7 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 1641db33a994..3395ca7bcadf 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 dccfa44c2d71..2be563cba72b 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 86d8da0cbd02..e057768f68b4 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 cb9711ea8c6c..d8e03c74ca76 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;