Commit 62859d82 authored by Dmitry Torokhov's avatar Dmitry Torokhov

[NET] Attach inner qdiscs to TBF

parent ce8b73b4
...@@ -61,7 +61,7 @@ struct Qdisc_ops ...@@ -61,7 +61,7 @@ struct Qdisc_ops
int (*enqueue)(struct sk_buff *, struct Qdisc *); int (*enqueue)(struct sk_buff *, struct Qdisc *);
struct sk_buff * (*dequeue)(struct Qdisc *); struct sk_buff * (*dequeue)(struct Qdisc *);
int (*requeue)(struct sk_buff *, 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); int (*init)(struct Qdisc *, struct rtattr *arg);
void (*reset)(struct Qdisc *); void (*reset)(struct Qdisc *);
......
...@@ -641,6 +641,7 @@ EXPORT_SYMBOL(qdisc_tree_lock); ...@@ -641,6 +641,7 @@ EXPORT_SYMBOL(qdisc_tree_lock);
#ifdef CONFIG_NET_SCHED #ifdef CONFIG_NET_SCHED
PSCHED_EXPORTLIST; PSCHED_EXPORTLIST;
EXPORT_SYMBOL(pfifo_qdisc_ops); EXPORT_SYMBOL(pfifo_qdisc_ops);
EXPORT_SYMBOL(bfifo_qdisc_ops);
EXPORT_SYMBOL(register_qdisc); EXPORT_SYMBOL(register_qdisc);
EXPORT_SYMBOL(unregister_qdisc); EXPORT_SYMBOL(unregister_qdisc);
EXPORT_SYMBOL(qdisc_get_rtab); EXPORT_SYMBOL(qdisc_get_rtab);
......
...@@ -545,15 +545,16 @@ static int atm_tc_requeue(struct sk_buff *skb,struct Qdisc *sch) ...@@ -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_qdisc_data *p = PRIV(sch);
struct atm_flow_data *flow; struct atm_flow_data *flow;
unsigned int len;
DPRINTK("atm_tc_drop(sch %p,[qdisc %p])\n",sch,p); DPRINTK("atm_tc_drop(sch %p,[qdisc %p])\n",sch,p);
for (flow = p->flows; flow; flow = flow->next) for (flow = p->flows; flow; flow = flow->next)
if (flow->q->ops->drop && flow->q->ops->drop(flow->q)) if (flow->q->ops->drop && (len = flow->q->ops->drop(flow->q)))
return 1; return len;
return 0; return 0;
} }
......
...@@ -1231,11 +1231,12 @@ static void cbq_link_class(struct cbq_class *this) ...@@ -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_sched_data *q = (struct cbq_sched_data *)sch->data;
struct cbq_class *cl, *cl_head; struct cbq_class *cl, *cl_head;
int prio; int prio;
unsigned int len;
for (prio = TC_CBQ_MAXPRIO; prio >= 0; prio--) { for (prio = TC_CBQ_MAXPRIO; prio >= 0; prio--) {
if ((cl_head = q->active[prio]) == NULL) if ((cl_head = q->active[prio]) == NULL)
...@@ -1243,9 +1244,9 @@ static int cbq_drop(struct Qdisc* sch) ...@@ -1243,9 +1244,9 @@ static int cbq_drop(struct Qdisc* sch)
cl = cl_head; cl = cl_head;
do { 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--; sch->q.qlen--;
return 1; return len;
} }
} while ((cl = cl->next_alive) != cl_head); } while ((cl = cl->next_alive) != cl_head);
} }
......
...@@ -302,17 +302,18 @@ static int dsmark_requeue(struct sk_buff *skb,struct Qdisc *sch) ...@@ -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); struct dsmark_qdisc_data *p = PRIV(sch);
unsigned int len;
DPRINTK("dsmark_reset(sch %p,[qdisc %p])\n",sch,p); DPRINTK("dsmark_reset(sch %p,[qdisc %p])\n",sch,p);
if (!p->q->ops->drop) if (!p->q->ops->drop)
return 0; return 0;
if (!p->q->ops->drop(p->q)) if (!(len = p->q->ops->drop(p->q)))
return 0; return 0;
sch->q.qlen--; sch->q.qlen--;
return 1; return len;
} }
......
...@@ -81,16 +81,17 @@ bfifo_dequeue(struct Qdisc* sch) ...@@ -81,16 +81,17 @@ bfifo_dequeue(struct Qdisc* sch)
return skb; return skb;
} }
static int static unsigned int
fifo_drop(struct Qdisc* sch) fifo_drop(struct Qdisc* sch)
{ {
struct sk_buff *skb; struct sk_buff *skb;
skb = __skb_dequeue_tail(&sch->q); skb = __skb_dequeue_tail(&sch->q);
if (skb) { if (skb) {
sch->stats.backlog -= skb->len; unsigned int len = skb->len;
sch->stats.backlog -= len;
kfree_skb(skb); kfree_skb(skb);
return 1; return len;
} }
return 0; return 0;
} }
......
...@@ -259,8 +259,7 @@ gred_dequeue(struct Qdisc* sch) ...@@ -259,8 +259,7 @@ gred_dequeue(struct Qdisc* sch)
return NULL; return NULL;
} }
static int static unsigned int gred_drop(struct Qdisc* sch)
gred_drop(struct Qdisc* sch)
{ {
struct sk_buff *skb; struct sk_buff *skb;
...@@ -269,20 +268,21 @@ gred_drop(struct Qdisc* sch) ...@@ -269,20 +268,21 @@ gred_drop(struct Qdisc* sch)
skb = __skb_dequeue_tail(&sch->q); skb = __skb_dequeue_tail(&sch->q);
if (skb) { if (skb) {
sch->stats.backlog -= skb->len; unsigned int len = skb->len;
sch->stats.backlog -= len;
sch->stats.drops++; sch->stats.drops++;
q= t->tab[(skb->tc_index&0xf)]; q= t->tab[(skb->tc_index&0xf)];
if (q) { if (q) {
q->backlog -= skb->len; q->backlog -= len;
q->other++; q->other++;
if (!q->backlog && !t->eqp) if (!q->backlog && !t->eqp)
PSCHED_GET_TIME(q->qidlestart); PSCHED_GET_TIME(q->qidlestart);
} else { } else {
D2PRINTK("gred_dequeue: skb has bad tcindex %x\n",skb->tc_index&0xf); D2PRINTK("gred_dequeue: skb has bad tcindex %x\n",skb->tc_index&0xf);
} }
kfree_skb(skb); kfree_skb(skb);
return 1; return len;
} }
q=t->tab[t->def]; q=t->tab[t->def];
......
...@@ -1059,7 +1059,7 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch) ...@@ -1059,7 +1059,7 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch)
} }
/* try to drop from each class (by prio) until one succeed */ /* 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; struct htb_sched *q = (struct htb_sched *)sch->data;
int prio; int prio;
...@@ -1067,14 +1067,15 @@ static int htb_drop(struct Qdisc* sch) ...@@ -1067,14 +1067,15 @@ static int htb_drop(struct Qdisc* sch)
for (prio = TC_HTB_NUMPRIO - 1; prio >= 0; prio--) { for (prio = TC_HTB_NUMPRIO - 1; prio >= 0; prio--) {
struct list_head *p; struct list_head *p;
list_for_each (p,q->drops+prio) { list_for_each (p,q->drops+prio) {
struct htb_class *cl = list_entry(p,struct htb_class, struct htb_class *cl = list_entry(p, struct htb_class,
un.leaf.drop_list); un.leaf.drop_list);
unsigned int len;
if (cl->un.leaf.q->ops->drop && 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--; sch->q.qlen--;
if (!cl->un.leaf.q->q.qlen) if (!cl->un.leaf.q->q.qlen)
htb_deactivate (q,cl); htb_deactivate (q,cl);
return 1; return len;
} }
} }
} }
......
...@@ -190,7 +190,7 @@ static int ingress_requeue(struct sk_buff *skb,struct Qdisc *sch) ...@@ -190,7 +190,7 @@ static int ingress_requeue(struct sk_buff *skb,struct Qdisc *sch)
return 0; return 0;
} }
static int ingress_drop(struct Qdisc *sch) static unsigned int ingress_drop(struct Qdisc *sch)
{ {
#ifdef DEBUG_INGRESS #ifdef DEBUG_INGRESS
struct ingress_qdisc_data *p = PRIV(sch); struct ingress_qdisc_data *p = PRIV(sch);
......
...@@ -124,18 +124,18 @@ prio_dequeue(struct Qdisc* sch) ...@@ -124,18 +124,18 @@ prio_dequeue(struct Qdisc* sch)
} }
static int static unsigned int prio_drop(struct Qdisc* sch)
prio_drop(struct Qdisc* sch)
{ {
struct prio_sched_data *q = (struct prio_sched_data *)sch->data; struct prio_sched_data *q = (struct prio_sched_data *)sch->data;
int prio; int prio;
unsigned int len;
struct Qdisc *qdisc; struct Qdisc *qdisc;
for (prio = q->bands-1; prio >= 0; prio--) { for (prio = q->bands-1; prio >= 0; prio--) {
qdisc = q->queues[prio]; qdisc = q->queues[prio];
if (qdisc->ops->drop(qdisc)) { if ((len = qdisc->ops->drop(qdisc)) != 0) {
sch->q.qlen--; sch->q.qlen--;
return 1; return len;
} }
} }
return 0; return 0;
......
...@@ -342,19 +342,19 @@ red_dequeue(struct Qdisc* sch) ...@@ -342,19 +342,19 @@ red_dequeue(struct Qdisc* sch)
return NULL; return NULL;
} }
static int static unsigned int red_drop(struct Qdisc* sch)
red_drop(struct Qdisc* sch)
{ {
struct sk_buff *skb; struct sk_buff *skb;
struct red_sched_data *q = (struct red_sched_data *)sch->data; struct red_sched_data *q = (struct red_sched_data *)sch->data;
skb = __skb_dequeue_tail(&sch->q); skb = __skb_dequeue_tail(&sch->q);
if (skb) { if (skb) {
sch->stats.backlog -= skb->len; unsigned int len = skb->len;
sch->stats.backlog -= len;
sch->stats.drops++; sch->stats.drops++;
q->st.other++; q->st.other++;
kfree_skb(skb); kfree_skb(skb);
return 1; return len;
} }
PSCHED_GET_TIME(q->qidlestart); PSCHED_GET_TIME(q->qidlestart);
return 0; return 0;
......
...@@ -209,11 +209,12 @@ static inline void sfq_inc(struct sfq_sched_data *q, sfq_index x) ...@@ -209,11 +209,12 @@ static inline void sfq_inc(struct sfq_sched_data *q, sfq_index x)
sfq_link(q, 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; struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
sfq_index d = q->max_depth; sfq_index d = q->max_depth;
struct sk_buff *skb; struct sk_buff *skb;
unsigned int len;
/* Queue is full! Find the longest slot and /* Queue is full! Find the longest slot and
drop a packet from it */ drop a packet from it */
...@@ -221,12 +222,13 @@ static int sfq_drop(struct Qdisc *sch) ...@@ -221,12 +222,13 @@ static int sfq_drop(struct Qdisc *sch)
if (d > 1) { if (d > 1) {
sfq_index x = q->dep[d+SFQ_DEPTH].next; sfq_index x = q->dep[d+SFQ_DEPTH].next;
skb = q->qs[x].prev; skb = q->qs[x].prev;
len = skb->len;
__skb_unlink(skb, &q->qs[x]); __skb_unlink(skb, &q->qs[x]);
kfree_skb(skb); kfree_skb(skb);
sfq_dec(q, x); sfq_dec(q, x);
sch->q.qlen--; sch->q.qlen--;
sch->stats.drops++; sch->stats.drops++;
return 1; return len;
} }
if (d == 1) { if (d == 1) {
...@@ -235,13 +237,14 @@ static int sfq_drop(struct Qdisc *sch) ...@@ -235,13 +237,14 @@ static int sfq_drop(struct Qdisc *sch)
q->next[q->tail] = q->next[d]; q->next[q->tail] = q->next[d];
q->allot[q->next[d]] += q->quantum; q->allot[q->next[d]] += q->quantum;
skb = q->qs[d].prev; skb = q->qs[d].prev;
len = skb->len;
__skb_unlink(skb, &q->qs[d]); __skb_unlink(skb, &q->qs[d]);
kfree_skb(skb); kfree_skb(skb);
sfq_dec(q, d); sfq_dec(q, d);
sch->q.qlen--; sch->q.qlen--;
q->ht[q->hash[d]] = SFQ_DEPTH; q->ht[q->hash[d]] = SFQ_DEPTH;
sch->stats.drops++; sch->stats.drops++;
return 1; return len;
} }
return 0; return 0;
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
* *
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * 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 ...@@ -123,62 +125,63 @@ struct tbf_sched_data
long ptokens; /* Current number of P tokens */ long ptokens; /* Current number of P tokens */
psched_time_t t_c; /* Time check-point */ psched_time_t t_c; /* Time check-point */
struct timer_list wd_timer; /* Watchdog timer */ 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(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]) #define L2T_P(q,L) ((q)->P_tab->data[(L)>>(q)->P_tab->rate.cell_log])
static int static int tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch)
tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch)
{ {
struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
int ret;
if (skb->len > q->max_size) if (skb->len > q->max_size || sch->stats.backlog + skb->len > q->limit) {
goto drop; sch->stats.drops++;
__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++;
#ifdef CONFIG_NET_CLS_POLICE #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 #endif
kfree_skb(skb); kfree_skb(skb);
return NET_XMIT_DROP;
} return NET_XMIT_DROP;
}
static int
tbf_requeue(struct sk_buff *skb, struct Qdisc* sch) if ((ret = q->qdisc->enqueue(skb, q->qdisc)) != 0) {
{ sch->stats.drops++;
__skb_queue_head(&sch->q, skb); return ret;
}
sch->q.qlen++;
sch->stats.backlog += skb->len; sch->stats.backlog += skb->len;
sch->stats.bytes += skb->len;
sch->stats.packets++;
return 0; return 0;
} }
static int static int tbf_requeue(struct sk_buff *skb, struct Qdisc* sch)
tbf_drop(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); static unsigned int tbf_drop(struct Qdisc* sch)
if (skb) { {
sch->stats.backlog -= skb->len; 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++; sch->stats.drops++;
kfree_skb(skb);
return 1;
} }
return 0; return len;
} }
static void tbf_watchdog(unsigned long arg) static void tbf_watchdog(unsigned long arg)
...@@ -189,19 +192,19 @@ static void tbf_watchdog(unsigned long arg) ...@@ -189,19 +192,19 @@ static void tbf_watchdog(unsigned long arg)
netif_schedule(sch->dev); netif_schedule(sch->dev);
} }
static struct sk_buff * static struct sk_buff *tbf_dequeue(struct Qdisc* sch)
tbf_dequeue(struct Qdisc* sch)
{ {
struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
struct sk_buff *skb; struct sk_buff *skb;
skb = __skb_dequeue(&sch->q); skb = q->qdisc->dequeue(q->qdisc);
if (skb) { if (skb) {
psched_time_t now; psched_time_t now;
long toks; long toks;
long ptoks = 0; long ptoks = 0;
unsigned int len = skb->len;
PSCHED_GET_TIME(now); PSCHED_GET_TIME(now);
toks = PSCHED_TDIFF_SAFE(now, q->t_c, q->buffer, 0); toks = PSCHED_TDIFF_SAFE(now, q->t_c, q->buffer, 0);
...@@ -210,18 +213,19 @@ tbf_dequeue(struct Qdisc* sch) ...@@ -210,18 +213,19 @@ tbf_dequeue(struct Qdisc* sch)
ptoks = toks + q->ptokens; ptoks = toks + q->ptokens;
if (ptoks > (long)q->mtu) if (ptoks > (long)q->mtu)
ptoks = q->mtu; ptoks = q->mtu;
ptoks -= L2T_P(q, skb->len); ptoks -= L2T_P(q, len);
} }
toks += q->tokens; toks += q->tokens;
if (toks > (long)q->buffer) if (toks > (long)q->buffer)
toks = q->buffer; toks = q->buffer;
toks -= L2T(q, skb->len); toks -= L2T(q, len);
if ((toks|ptoks) >= 0) { if ((toks|ptoks) >= 0) {
q->t_c = now; q->t_c = now;
q->tokens = toks; q->tokens = toks;
q->ptokens = ptoks; q->ptokens = ptoks;
sch->stats.backlog -= skb->len; sch->stats.backlog -= len;
sch->q.qlen--;
sch->flags &= ~TCQ_F_THROTTLED; sch->flags &= ~TCQ_F_THROTTLED;
return skb; return skb;
} }
...@@ -245,20 +249,25 @@ tbf_dequeue(struct Qdisc* sch) ...@@ -245,20 +249,25 @@ tbf_dequeue(struct Qdisc* sch)
This is the main idea of all FQ algorithms This is the main idea of all FQ algorithms
(cf. CSZ, HPFQ, HFSC) (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->flags |= TCQ_F_THROTTLED;
sch->stats.overlimits++; sch->stats.overlimits++;
} }
return NULL; 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; struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
qdisc_reset(q->qdisc);
skb_queue_purge(&sch->q); skb_queue_purge(&sch->q);
sch->stats.backlog = 0; sch->stats.backlog = 0;
PSCHED_GET_TIME(q->t_c); PSCHED_GET_TIME(q->t_c);
...@@ -268,6 +277,31 @@ tbf_reset(struct Qdisc* sch) ...@@ -268,6 +277,31 @@ tbf_reset(struct Qdisc* sch)
del_timer(&q->wd_timer); 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) static int tbf_change(struct Qdisc* sch, struct rtattr *opt)
{ {
int err = -EINVAL; int err = -EINVAL;
...@@ -276,6 +310,7 @@ static int tbf_change(struct Qdisc* sch, struct rtattr *opt) ...@@ -276,6 +310,7 @@ static int tbf_change(struct Qdisc* sch, struct rtattr *opt)
struct tc_tbf_qopt *qopt; struct tc_tbf_qopt *qopt;
struct qdisc_rate_table *rtab = NULL; struct qdisc_rate_table *rtab = NULL;
struct qdisc_rate_table *ptab = NULL; struct qdisc_rate_table *ptab = NULL;
struct Qdisc *child = NULL;
int max_size,n; int max_size,n;
if (rtattr_parse(tb, TCA_TBF_PTAB, RTA_DATA(opt), RTA_PAYLOAD(opt)) || 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) ...@@ -308,8 +343,14 @@ static int tbf_change(struct Qdisc* sch, struct rtattr *opt)
} }
if (max_size < 0) if (max_size < 0)
goto done; goto done;
if (q->qdisc == &noop_qdisc) {
if ((child = tbf_create_dflt_qdisc(sch->dev, qopt->limit)) == NULL)
goto done;
}
sch_tree_lock(sch); sch_tree_lock(sch);
if (child) q->qdisc = child;
q->limit = qopt->limit; q->limit = qopt->limit;
q->mtu = qopt->mtu; q->mtu = qopt->mtu;
q->max_size = max_size; q->max_size = max_size;
...@@ -339,6 +380,8 @@ static int tbf_init(struct Qdisc* sch, struct rtattr *opt) ...@@ -339,6 +380,8 @@ static int tbf_init(struct Qdisc* sch, struct rtattr *opt)
init_timer(&q->wd_timer); init_timer(&q->wd_timer);
q->wd_timer.function = tbf_watchdog; q->wd_timer.function = tbf_watchdog;
q->wd_timer.data = (unsigned long)sch; q->wd_timer.data = (unsigned long)sch;
q->qdisc = &noop_qdisc;
return tbf_change(sch, opt); return tbf_change(sch, opt);
} }
...@@ -353,6 +396,9 @@ static void tbf_destroy(struct Qdisc *sch) ...@@ -353,6 +396,9 @@ static void tbf_destroy(struct Qdisc *sch)
qdisc_put_rtab(q->P_tab); qdisc_put_rtab(q->P_tab);
if (q->R_tab) if (q->R_tab)
qdisc_put_rtab(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) static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb)
...@@ -383,9 +429,92 @@ static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb) ...@@ -383,9 +429,92 @@ static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb)
return -1; 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 = { struct Qdisc_ops tbf_qdisc_ops = {
.next = NULL, .next = NULL,
.cl_ops = NULL, .cl_ops = &tbf_class_ops,
.id = "tbf", .id = "tbf",
.priv_size = sizeof(struct tbf_sched_data), .priv_size = sizeof(struct tbf_sched_data),
.enqueue = tbf_enqueue, .enqueue = tbf_enqueue,
...@@ -397,6 +526,7 @@ struct Qdisc_ops tbf_qdisc_ops = { ...@@ -397,6 +526,7 @@ struct Qdisc_ops tbf_qdisc_ops = {
.destroy = tbf_destroy, .destroy = tbf_destroy,
.change = tbf_change, .change = tbf_change,
.dump = tbf_dump, .dump = tbf_dump,
.owner = THIS_MODULE,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment