net_sched: walk through all child classes in tc_bind_tclass()
In a complex TC class hierarchy like this:
tc qdisc add dev eth0 root handle 1:0 cbq bandwidth 100Mbit \
avpkt 1000 cell 8
tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 100Mbit \
rate 6Mbit weight 0.6Mbit prio 8 allot 1514 cell 8 maxburst 20 \
avpkt 1000 bounded
tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \
sport 80 0xffff flowid 1:3
tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \
sport 25 0xffff flowid 1:4
tc class add dev eth0 parent 1:1 classid 1:3 cbq bandwidth 100Mbit \
rate 5Mbit weight 0.5Mbit prio 5 allot 1514 cell 8 maxburst 20 \
avpkt 1000
tc class add dev eth0 parent 1:1 classid 1:4 cbq bandwidth 100Mbit \
rate 3Mbit weight 0.3Mbit prio 5 allot 1514 cell 8 maxburst 20 \
avpkt 1000
where filters are installed on qdisc 1:0, so we can't merely
search from class 1:1 when creating class 1:3 and class 1:4. We have
to walk through all the child classes of the direct parent qdisc.
Otherwise we would miss filters those need reverse binding.
Fixes: 07d79fc7d9
("net_sched: add reverse binding for tc class")
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
2e24cd7555
Коммит
760d228e32
|
@ -1910,22 +1910,24 @@ static int tcf_node_bind(struct tcf_proto *tp, void *n, struct tcf_walker *arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid,
|
||||
unsigned long new_cl)
|
||||
struct tc_bind_class_args {
|
||||
struct qdisc_walker w;
|
||||
unsigned long new_cl;
|
||||
u32 portid;
|
||||
u32 clid;
|
||||
};
|
||||
|
||||
static int tc_bind_class_walker(struct Qdisc *q, unsigned long cl,
|
||||
struct qdisc_walker *w)
|
||||
{
|
||||
struct tc_bind_class_args *a = (struct tc_bind_class_args *)w;
|
||||
const struct Qdisc_class_ops *cops = q->ops->cl_ops;
|
||||
struct tcf_block *block;
|
||||
struct tcf_chain *chain;
|
||||
unsigned long cl;
|
||||
|
||||
cl = cops->find(q, portid);
|
||||
if (!cl)
|
||||
return;
|
||||
if (!cops->tcf_block)
|
||||
return;
|
||||
block = cops->tcf_block(q, cl, NULL);
|
||||
if (!block)
|
||||
return;
|
||||
return 0;
|
||||
for (chain = tcf_get_next_chain(block, NULL);
|
||||
chain;
|
||||
chain = tcf_get_next_chain(block, chain)) {
|
||||
|
@ -1936,12 +1938,29 @@ static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid,
|
|||
struct tcf_bind_args arg = {};
|
||||
|
||||
arg.w.fn = tcf_node_bind;
|
||||
arg.classid = clid;
|
||||
arg.classid = a->clid;
|
||||
arg.base = cl;
|
||||
arg.cl = new_cl;
|
||||
arg.cl = a->new_cl;
|
||||
tp->ops->walk(tp, &arg.w, true);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid,
|
||||
unsigned long new_cl)
|
||||
{
|
||||
const struct Qdisc_class_ops *cops = q->ops->cl_ops;
|
||||
struct tc_bind_class_args args = {};
|
||||
|
||||
if (!cops->tcf_block)
|
||||
return;
|
||||
args.portid = portid;
|
||||
args.clid = clid;
|
||||
args.new_cl = new_cl;
|
||||
args.w.fn = tc_bind_class_walker;
|
||||
q->ops->cl_ops->walk(q, &args.w);
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
Загрузка…
Ссылка в новой задаче