Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

authored by

Cong Wang and committed by
David S. Miller
760d228e 2e24cd75

+30 -11
+30 -11
net/sched/sch_api.c
··· 1910 1910 return 0; 1911 1911 } 1912 1912 1913 - static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid, 1914 - unsigned long new_cl) 1913 + struct tc_bind_class_args { 1914 + struct qdisc_walker w; 1915 + unsigned long new_cl; 1916 + u32 portid; 1917 + u32 clid; 1918 + }; 1919 + 1920 + static int tc_bind_class_walker(struct Qdisc *q, unsigned long cl, 1921 + struct qdisc_walker *w) 1915 1922 { 1923 + struct tc_bind_class_args *a = (struct tc_bind_class_args *)w; 1916 1924 const struct Qdisc_class_ops *cops = q->ops->cl_ops; 1917 1925 struct tcf_block *block; 1918 1926 struct tcf_chain *chain; 1919 - unsigned long cl; 1920 1927 1921 - cl = cops->find(q, portid); 1922 - if (!cl) 1923 - return; 1924 - if (!cops->tcf_block) 1925 - return; 1926 1928 block = cops->tcf_block(q, cl, NULL); 1927 1929 if (!block) 1928 - return; 1930 + return 0; 1929 1931 for (chain = tcf_get_next_chain(block, NULL); 1930 1932 chain; 1931 1933 chain = tcf_get_next_chain(block, chain)) { ··· 1938 1936 struct tcf_bind_args arg = {}; 1939 1937 1940 1938 arg.w.fn = tcf_node_bind; 1941 - arg.classid = clid; 1939 + arg.classid = a->clid; 1942 1940 arg.base = cl; 1943 - arg.cl = new_cl; 1941 + arg.cl = a->new_cl; 1944 1942 tp->ops->walk(tp, &arg.w, true); 1945 1943 } 1946 1944 } 1945 + 1946 + return 0; 1947 + } 1948 + 1949 + static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid, 1950 + unsigned long new_cl) 1951 + { 1952 + const struct Qdisc_class_ops *cops = q->ops->cl_ops; 1953 + struct tc_bind_class_args args = {}; 1954 + 1955 + if (!cops->tcf_block) 1956 + return; 1957 + args.portid = portid; 1958 + args.clid = clid; 1959 + args.new_cl = new_cl; 1960 + args.w.fn = tc_bind_class_walker; 1961 + q->ops->cl_ops->walk(q, &args.w); 1947 1962 } 1948 1963 1949 1964 #else