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:
Cong Wang 2020-01-23 17:27:08 -08:00 коммит произвёл David S. Miller
Родитель 2e24cd7555
Коммит 760d228e32
1 изменённых файлов: 30 добавлений и 11 удалений

Просмотреть файл

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