net_sched: walk through all child classes in tc_bind_tclass()
authorCong Wang <xiyou.wangcong@gmail.com>
Fri, 24 Jan 2020 01:27:08 +0000 (17:27 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 1 Feb 2020 09:34:39 +0000 (09:34 +0000)
[ Upstream commit 760d228e322e99cdf6d81b4b60a268b8f13cf67a ]

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: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/sched/sch_api.c

index 943ad34..5079412 100644 (file)
@@ -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