[net] net_sched: walk through all child classes in tc_bind_tclass()
diff mbox series

Message ID 20200124012708.29366-1-xiyou.wangcong@gmail.com
State Accepted
Delegated to: David Miller
Headers show
Series
  • [net] net_sched: walk through all child classes in tc_bind_tclass()
Related show

Commit Message

Cong Wang Jan. 24, 2020, 1:27 a.m. UTC
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>
---
 net/sched/sch_api.c | 41 ++++++++++++++++++++++++++++++-----------
 1 file changed, 30 insertions(+), 11 deletions(-)

Comments

David Miller Jan. 27, 2020, 9:53 a.m. UTC | #1
From: Cong Wang <xiyou.wangcong@gmail.com>
Date: Thu, 23 Jan 2020 17:27:08 -0800

> 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>

Applied and queued up for -stable.

Patch
diff mbox series

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