diff options
| -rw-r--r-- | include/net/pkt_sched.h | 2 | ||||
| -rw-r--r-- | net/netsyms.c | 1 | ||||
| -rw-r--r-- | net/sched/sch_atm.c | 7 | ||||
| -rw-r--r-- | net/sched/sch_cbq.c | 7 | ||||
| -rw-r--r-- | net/sched/sch_dsmark.c | 9 | ||||
| -rw-r--r-- | net/sched/sch_fifo.c | 7 | ||||
| -rw-r--r-- | net/sched/sch_gred.c | 16 | ||||
| -rw-r--r-- | net/sched/sch_htb.c | 11 | ||||
| -rw-r--r-- | net/sched/sch_ingress.c | 2 | ||||
| -rw-r--r-- | net/sched/sch_prio.c | 8 | ||||
| -rw-r--r-- | net/sched/sch_red.c | 8 | ||||
| -rw-r--r-- | net/sched/sch_sfq.c | 9 | ||||
| -rw-r--r-- | net/sched/sch_tbf.c | 232 |
13 files changed, 229 insertions, 90 deletions
diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index eca2baddbc22..4c9ed3ad418d 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -61,7 +61,7 @@ struct Qdisc_ops int (*enqueue)(struct sk_buff *, struct Qdisc *); struct sk_buff * (*dequeue)(struct Qdisc *); int (*requeue)(struct sk_buff *, struct Qdisc *); - int (*drop)(struct Qdisc *); + unsigned int (*drop)(struct Qdisc *); int (*init)(struct Qdisc *, struct rtattr *arg); void (*reset)(struct Qdisc *); diff --git a/net/netsyms.c b/net/netsyms.c index aad457bd3d7c..f327ebe6c755 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -641,6 +641,7 @@ EXPORT_SYMBOL(qdisc_tree_lock); #ifdef CONFIG_NET_SCHED PSCHED_EXPORTLIST; EXPORT_SYMBOL(pfifo_qdisc_ops); +EXPORT_SYMBOL(bfifo_qdisc_ops); EXPORT_SYMBOL(register_qdisc); EXPORT_SYMBOL(unregister_qdisc); EXPORT_SYMBOL(qdisc_get_rtab); diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 94c349128df6..be15be08f0b6 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -545,15 +545,16 @@ static int atm_tc_requeue(struct sk_buff *skb,struct Qdisc *sch) } -static int atm_tc_drop(struct Qdisc *sch) +static unsigned int atm_tc_drop(struct Qdisc *sch) { struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow; + unsigned int len; DPRINTK("atm_tc_drop(sch %p,[qdisc %p])\n",sch,p); for (flow = p->flows; flow; flow = flow->next) - if (flow->q->ops->drop && flow->q->ops->drop(flow->q)) - return 1; + if (flow->q->ops->drop && (len = flow->q->ops->drop(flow->q))) + return len; return 0; } diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 620427492ed4..f7e5bef816dd 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1231,11 +1231,12 @@ static void cbq_link_class(struct cbq_class *this) } } -static int cbq_drop(struct Qdisc* sch) +static unsigned int cbq_drop(struct Qdisc* sch) { struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data; struct cbq_class *cl, *cl_head; int prio; + unsigned int len; for (prio = TC_CBQ_MAXPRIO; prio >= 0; prio--) { if ((cl_head = q->active[prio]) == NULL) @@ -1243,9 +1244,9 @@ static int cbq_drop(struct Qdisc* sch) cl = cl_head; do { - if (cl->q->ops->drop && cl->q->ops->drop(cl->q)) { + if (cl->q->ops->drop && (len = cl->q->ops->drop(cl->q))) { sch->q.qlen--; - return 1; + return len; } } while ((cl = cl->next_alive) != cl_head); } diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index 88274fa78087..ec9837bb9e5d 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -302,17 +302,18 @@ static int dsmark_requeue(struct sk_buff *skb,struct Qdisc *sch) } -static int dsmark_drop(struct Qdisc *sch) +static unsigned int dsmark_drop(struct Qdisc *sch) { struct dsmark_qdisc_data *p = PRIV(sch); - + unsigned int len; + DPRINTK("dsmark_reset(sch %p,[qdisc %p])\n",sch,p); if (!p->q->ops->drop) return 0; - if (!p->q->ops->drop(p->q)) + if (!(len = p->q->ops->drop(p->q))) return 0; sch->q.qlen--; - return 1; + return len; } diff --git a/net/sched/sch_fifo.c b/net/sched/sch_fifo.c index df193ebe19c5..9e49f95eb0ef 100644 --- a/net/sched/sch_fifo.c +++ b/net/sched/sch_fifo.c @@ -81,16 +81,17 @@ bfifo_dequeue(struct Qdisc* sch) return skb; } -static int +static unsigned int fifo_drop(struct Qdisc* sch) { struct sk_buff *skb; skb = __skb_dequeue_tail(&sch->q); if (skb) { - sch->stats.backlog -= skb->len; + unsigned int len = skb->len; + sch->stats.backlog -= len; kfree_skb(skb); - return 1; + return len; } return 0; } diff --git a/net/sched/sch_gred.c b/net/sched/sch_gred.c index f527f7746972..051c2527fcca 100644 --- a/net/sched/sch_gred.c +++ b/net/sched/sch_gred.c @@ -259,8 +259,7 @@ gred_dequeue(struct Qdisc* sch) return NULL; } -static int -gred_drop(struct Qdisc* sch) +static unsigned int gred_drop(struct Qdisc* sch) { struct sk_buff *skb; @@ -269,20 +268,21 @@ gred_drop(struct Qdisc* sch) skb = __skb_dequeue_tail(&sch->q); if (skb) { - sch->stats.backlog -= skb->len; + unsigned int len = skb->len; + sch->stats.backlog -= len; sch->stats.drops++; q= t->tab[(skb->tc_index&0xf)]; if (q) { - q->backlog -= skb->len; + q->backlog -= len; q->other++; if (!q->backlog && !t->eqp) PSCHED_GET_TIME(q->qidlestart); - } else { - D2PRINTK("gred_dequeue: skb has bad tcindex %x\n",skb->tc_index&0xf); - } + } else { + D2PRINTK("gred_dequeue: skb has bad tcindex %x\n",skb->tc_index&0xf); + } kfree_skb(skb); - return 1; + return len; } q=t->tab[t->def]; diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index ef4ea4452c5b..6063ef43e173 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1059,7 +1059,7 @@ fin: } /* try to drop from each class (by prio) until one succeed */ -static int htb_drop(struct Qdisc* sch) +static unsigned int htb_drop(struct Qdisc* sch) { struct htb_sched *q = (struct htb_sched *)sch->data; int prio; @@ -1067,14 +1067,15 @@ static int htb_drop(struct Qdisc* sch) for (prio = TC_HTB_NUMPRIO - 1; prio >= 0; prio--) { struct list_head *p; list_for_each (p,q->drops+prio) { - struct htb_class *cl = list_entry(p,struct htb_class, - un.leaf.drop_list); + struct htb_class *cl = list_entry(p, struct htb_class, + un.leaf.drop_list); + unsigned int len; if (cl->un.leaf.q->ops->drop && - cl->un.leaf.q->ops->drop(cl->un.leaf.q)) { + (len = cl->un.leaf.q->ops->drop(cl->un.leaf.q))) { sch->q.qlen--; if (!cl->un.leaf.q->q.qlen) htb_deactivate (q,cl); - return 1; + return len; } } } diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index 2b77133fb9fc..5a63a85d84b2 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -190,7 +190,7 @@ static int ingress_requeue(struct sk_buff *skb,struct Qdisc *sch) return 0; } -static int ingress_drop(struct Qdisc *sch) +static unsigned int ingress_drop(struct Qdisc *sch) { #ifdef DEBUG_INGRESS struct ingress_qdisc_data *p = PRIV(sch); diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 2bf080a641b5..c09a7f95050a 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -124,18 +124,18 @@ prio_dequeue(struct Qdisc* sch) } -static int -prio_drop(struct Qdisc* sch) +static unsigned int prio_drop(struct Qdisc* sch) { struct prio_sched_data *q = (struct prio_sched_data *)sch->data; int prio; + unsigned int len; struct Qdisc *qdisc; for (prio = q->bands-1; prio >= 0; prio--) { qdisc = q->queues[prio]; - if (qdisc->ops->drop(qdisc)) { + if ((len = qdisc->ops->drop(qdisc)) != 0) { sch->q.qlen--; - return 1; + return len; } } return 0; diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index e5fd355f27b6..b2e8956418ae 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -342,19 +342,19 @@ red_dequeue(struct Qdisc* sch) return NULL; } -static int -red_drop(struct Qdisc* sch) +static unsigned int red_drop(struct Qdisc* sch) { struct sk_buff *skb; struct red_sched_data *q = (struct red_sched_data *)sch->data; skb = __skb_dequeue_tail(&sch->q); if (skb) { - sch->stats.backlog -= skb->len; + unsigned int len = skb->len; + sch->stats.backlog -= len; sch->stats.drops++; q->st.other++; kfree_skb(skb); - return 1; + return len; } PSCHED_GET_TIME(q->qidlestart); return 0; diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index a544be3aa721..c5802d8b2cc6 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -209,11 +209,12 @@ static inline void sfq_inc(struct sfq_sched_data *q, sfq_index x) sfq_link(q, x); } -static int sfq_drop(struct Qdisc *sch) +static unsigned int sfq_drop(struct Qdisc *sch) { struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data; sfq_index d = q->max_depth; struct sk_buff *skb; + unsigned int len; /* Queue is full! Find the longest slot and drop a packet from it */ @@ -221,12 +222,13 @@ static int sfq_drop(struct Qdisc *sch) if (d > 1) { sfq_index x = q->dep[d+SFQ_DEPTH].next; skb = q->qs[x].prev; + len = skb->len; __skb_unlink(skb, &q->qs[x]); kfree_skb(skb); sfq_dec(q, x); sch->q.qlen--; sch->stats.drops++; - return 1; + return len; } if (d == 1) { @@ -235,13 +237,14 @@ static int sfq_drop(struct Qdisc *sch) q->next[q->tail] = q->next[d]; q->allot[q->next[d]] += q->quantum; skb = q->qs[d].prev; + len = skb->len; __skb_unlink(skb, &q->qs[d]); kfree_skb(skb); sfq_dec(q, d); sch->q.qlen--; q->ht[q->hash[d]] = SFQ_DEPTH; sch->stats.drops++; - return 1; + return len; } return 0; diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 382bc76a71d5..c115df752163 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -7,6 +7,8 @@ * 2 of the License, or (at your option) any later version. * * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * Dmitry Torokhov <dtor@mail.ru> - allow attaching inner qdiscs - + * original idea by Martin Devera * */ @@ -123,62 +125,63 @@ struct tbf_sched_data long ptokens; /* Current number of P tokens */ psched_time_t t_c; /* Time check-point */ struct timer_list wd_timer; /* Watchdog timer */ + struct Qdisc *qdisc; /* Inner qdisc, default - bfifo queue */ }; #define L2T(q,L) ((q)->R_tab->data[(L)>>(q)->R_tab->rate.cell_log]) #define L2T_P(q,L) ((q)->P_tab->data[(L)>>(q)->P_tab->rate.cell_log]) -static int -tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch) +static int tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch) { struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + int ret; - if (skb->len > q->max_size) - goto drop; - __skb_queue_tail(&sch->q, skb); - if ((sch->stats.backlog += skb->len) <= q->limit) { - sch->stats.bytes += skb->len; - sch->stats.packets++; - return 0; - } - - /* Drop action: undo the things that we just did, - * i.e. make tail drop - */ - - __skb_unlink(skb, &sch->q); - sch->stats.backlog -= skb->len; - -drop: - sch->stats.drops++; + if (skb->len > q->max_size || sch->stats.backlog + skb->len > q->limit) { + sch->stats.drops++; #ifdef CONFIG_NET_CLS_POLICE - if (sch->reshape_fail==NULL || sch->reshape_fail(skb, sch)) + if (sch->reshape_fail == NULL || sch->reshape_fail(skb, sch)) #endif - kfree_skb(skb); - return NET_XMIT_DROP; -} - -static int -tbf_requeue(struct sk_buff *skb, struct Qdisc* sch) -{ - __skb_queue_head(&sch->q, skb); + kfree_skb(skb); + + return NET_XMIT_DROP; + } + + if ((ret = q->qdisc->enqueue(skb, q->qdisc)) != 0) { + sch->stats.drops++; + return ret; + } + + sch->q.qlen++; sch->stats.backlog += skb->len; + sch->stats.bytes += skb->len; + sch->stats.packets++; return 0; } -static int -tbf_drop(struct Qdisc* sch) +static int tbf_requeue(struct sk_buff *skb, struct Qdisc* sch) { - struct sk_buff *skb; + struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + int ret; + + if ((ret = q->qdisc->ops->requeue(skb, q->qdisc)) == 0) { + sch->q.qlen++; + sch->stats.backlog += skb->len; + } + + return ret; +} - skb = __skb_dequeue_tail(&sch->q); - if (skb) { - sch->stats.backlog -= skb->len; +static unsigned int tbf_drop(struct Qdisc* sch) +{ + struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + unsigned int len; + + if ((len = q->qdisc->ops->drop(q->qdisc)) != 0) { + sch->q.qlen--; + sch->stats.backlog -= len; sch->stats.drops++; - kfree_skb(skb); - return 1; } - return 0; + return len; } static void tbf_watchdog(unsigned long arg) @@ -189,19 +192,19 @@ static void tbf_watchdog(unsigned long arg) netif_schedule(sch->dev); } -static struct sk_buff * -tbf_dequeue(struct Qdisc* sch) +static struct sk_buff *tbf_dequeue(struct Qdisc* sch) { struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; struct sk_buff *skb; - skb = __skb_dequeue(&sch->q); + skb = q->qdisc->dequeue(q->qdisc); if (skb) { psched_time_t now; long toks; long ptoks = 0; - + unsigned int len = skb->len; + PSCHED_GET_TIME(now); toks = PSCHED_TDIFF_SAFE(now, q->t_c, q->buffer, 0); @@ -210,18 +213,19 @@ tbf_dequeue(struct Qdisc* sch) ptoks = toks + q->ptokens; if (ptoks > (long)q->mtu) ptoks = q->mtu; - ptoks -= L2T_P(q, skb->len); + ptoks -= L2T_P(q, len); } toks += q->tokens; if (toks > (long)q->buffer) toks = q->buffer; - toks -= L2T(q, skb->len); + toks -= L2T(q, len); if ((toks|ptoks) >= 0) { q->t_c = now; q->tokens = toks; q->ptokens = ptoks; - sch->stats.backlog -= skb->len; + sch->stats.backlog -= len; + sch->q.qlen--; sch->flags &= ~TCQ_F_THROTTLED; return skb; } @@ -245,20 +249,25 @@ tbf_dequeue(struct Qdisc* sch) This is the main idea of all FQ algorithms (cf. CSZ, HPFQ, HFSC) */ - __skb_queue_head(&sch->q, skb); - + + if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) { + /* When requeue fails skb is dropped */ + sch->q.qlen--; + sch->stats.backlog -= len; + sch->stats.drops++; + } + sch->flags |= TCQ_F_THROTTLED; sch->stats.overlimits++; } return NULL; } - -static void -tbf_reset(struct Qdisc* sch) +static void tbf_reset(struct Qdisc* sch) { struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + qdisc_reset(q->qdisc); skb_queue_purge(&sch->q); sch->stats.backlog = 0; PSCHED_GET_TIME(q->t_c); @@ -268,6 +277,31 @@ tbf_reset(struct Qdisc* sch) del_timer(&q->wd_timer); } +static struct Qdisc *tbf_create_dflt_qdisc(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 tbf_change(struct Qdisc* sch, struct rtattr *opt) { int err = -EINVAL; @@ -276,6 +310,7 @@ static int tbf_change(struct Qdisc* sch, struct rtattr *opt) struct tc_tbf_qopt *qopt; struct qdisc_rate_table *rtab = NULL; struct qdisc_rate_table *ptab = NULL; + struct Qdisc *child = NULL; int max_size,n; if (rtattr_parse(tb, TCA_TBF_PTAB, RTA_DATA(opt), RTA_PAYLOAD(opt)) || @@ -308,8 +343,14 @@ static int tbf_change(struct Qdisc* sch, struct rtattr *opt) } if (max_size < 0) goto done; + + if (q->qdisc == &noop_qdisc) { + if ((child = tbf_create_dflt_qdisc(sch->dev, qopt->limit)) == NULL) + goto done; + } sch_tree_lock(sch); + if (child) q->qdisc = child; q->limit = qopt->limit; q->mtu = qopt->mtu; q->max_size = max_size; @@ -339,6 +380,8 @@ static int tbf_init(struct Qdisc* sch, struct rtattr *opt) init_timer(&q->wd_timer); q->wd_timer.function = tbf_watchdog; q->wd_timer.data = (unsigned long)sch; + + q->qdisc = &noop_qdisc; return tbf_change(sch, opt); } @@ -353,6 +396,9 @@ static void tbf_destroy(struct Qdisc *sch) qdisc_put_rtab(q->P_tab); if (q->R_tab) qdisc_put_rtab(q->R_tab); + + qdisc_destroy(q->qdisc); + q->qdisc = &noop_qdisc; } static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb) @@ -383,9 +429,92 @@ rtattr_failure: return -1; } +static int tbf_dump_class(struct Qdisc *sch, unsigned long cl, + struct sk_buff *skb, struct tcmsg *tcm) +{ + struct tbf_sched_data *q = (struct tbf_sched_data*)sch->data; + + if (cl != 1) /* only one class */ + return -ENOENT; + + tcm->tcm_parent = TC_H_ROOT; + tcm->tcm_handle = 1; + tcm->tcm_info = q->qdisc->handle; + + return 0; +} + +static int tbf_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, + struct Qdisc **old) +{ + struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + + if (new == NULL) + new = &noop_qdisc; + + sch_tree_lock(sch); + *old = xchg(&q->qdisc, new); + qdisc_reset(*old); + sch_tree_unlock(sch); + + return 0; +} + +static struct Qdisc *tbf_leaf(struct Qdisc *sch, unsigned long arg) +{ + struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + return q->qdisc; +} + +static unsigned long tbf_get(struct Qdisc *sch, u32 classid) +{ + return 1; +} + +static void tbf_put(struct Qdisc *sch, unsigned long arg) +{ +} + +static int tbf_change_class(struct Qdisc *sch, u32 classid, u32 parentid, + struct rtattr **tca, unsigned long *arg) +{ + return -ENOSYS; +} + +static int tbf_delete(struct Qdisc *sch, unsigned long arg) +{ + return -ENOSYS; +} + +static void tbf_walk(struct Qdisc *sch, struct qdisc_walker *walker) +{ + struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + + if (!walker->stop) { + if (walker->count >= walker->skip) + if (walker->fn(sch, (unsigned long)q, walker) < 0) { + walker->stop = 1; + return; + } + walker->count++; + } +} + +static struct Qdisc_class_ops tbf_class_ops = +{ + .graft = tbf_graft, + .leaf = tbf_leaf, + .get = tbf_get, + .put = tbf_put, + .change = tbf_change_class, + .delete = tbf_delete, + .walk = tbf_walk, + .dump = tbf_dump_class, +}; + struct Qdisc_ops tbf_qdisc_ops = { .next = NULL, - .cl_ops = NULL, + .cl_ops = &tbf_class_ops, .id = "tbf", .priv_size = sizeof(struct tbf_sched_data), .enqueue = tbf_enqueue, @@ -397,6 +526,7 @@ struct Qdisc_ops tbf_qdisc_ops = { .destroy = tbf_destroy, .change = tbf_change, .dump = tbf_dump, + .owner = THIS_MODULE, }; |
