| From 3463cc422f3f26601dc5157ec8106c78c7e29bdc Mon Sep 17 00:00:00 2001 |
| From: Cong Wang <xiyou.wangcong@gmail.com> |
| Date: Thu, 23 Jan 2020 17:27:08 -0800 |
| Subject: [PATCH] net_sched: walk through all child classes in tc_bind_tclass() |
| |
| commit 760d228e322e99cdf6d81b4b60a268b8f13cf67a upstream. |
| |
| 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: 07d79fc7d94e ("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> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c |
| index 943ad3425380..50794125bf02 100644 |
| --- a/net/sched/sch_api.c |
| +++ b/net/sched/sch_api.c |
| @@ -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 |
| -- |
| 2.7.4 |
| |