Skip to content

Commit 5bc1701

Browse files
jpirkodavem330
authored andcommitted
net: sched: introduce multichain support for filters
Instead of having only one filter per block, introduce a list of chains for every block. Create chain 0 by default. UAPI is extended so the user can specify which chain he wants to change. If the new attribute is not specified, chain 0 is used. That allows to maintain backward compatibility. If chain does not exist and user wants to manipulate with it, new chain is created with specified index. Also, when last filter is removed from the chain, the chain is destroyed. Signed-off-by: Jiri Pirko <[email protected]> Acked-by: Jamal Hadi Salim <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent acb31fa commit 5bc1701

File tree

4 files changed

+98
-18
lines changed

4 files changed

+98
-18
lines changed

include/net/pkt_cls.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ int register_tcf_proto_ops(struct tcf_proto_ops *ops);
1818
int unregister_tcf_proto_ops(struct tcf_proto_ops *ops);
1919

2020
#ifdef CONFIG_NET_CLS
21+
struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index);
22+
void tcf_chain_put(struct tcf_chain *chain);
2123
int tcf_block_get(struct tcf_block **p_block,
2224
struct tcf_proto __rcu **p_filter_chain);
2325
void tcf_block_put(struct tcf_block *block);

include/net/sch_generic.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/pkt_cls.h>
99
#include <linux/percpu.h>
1010
#include <linux/dynamic_queue_limits.h>
11+
#include <linux/list.h>
1112
#include <net/gen_stats.h>
1213
#include <net/rtnetlink.h>
1314

@@ -236,7 +237,7 @@ struct tcf_proto {
236237
struct Qdisc *q;
237238
void *data;
238239
const struct tcf_proto_ops *ops;
239-
struct tcf_block *block;
240+
struct tcf_chain *chain;
240241
struct rcu_head rcu;
241242
};
242243

@@ -251,10 +252,14 @@ struct qdisc_skb_cb {
251252
struct tcf_chain {
252253
struct tcf_proto __rcu *filter_chain;
253254
struct tcf_proto __rcu **p_filter_chain;
255+
struct list_head list;
256+
struct tcf_block *block;
257+
u32 index; /* chain index */
258+
unsigned int refcnt;
254259
};
255260

256261
struct tcf_block {
257-
struct tcf_chain *chain;
262+
struct list_head chain_list;
258263
};
259264

260265
static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz)

include/uapi/linux/rtnetlink.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ enum {
549549
TCA_STAB,
550550
TCA_PAD,
551551
TCA_DUMP_INVISIBLE,
552+
TCA_CHAIN,
552553
__TCA_MAX
553554
};
554555

net/sched/cls_api.c

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ static inline u32 tcf_auto_prio(struct tcf_proto *tp)
129129

130130
static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol,
131131
u32 prio, u32 parent, struct Qdisc *q,
132-
struct tcf_block *block)
132+
struct tcf_chain *chain)
133133
{
134134
struct tcf_proto *tp;
135135
int err;
@@ -165,7 +165,7 @@ static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol,
165165
tp->prio = prio;
166166
tp->classid = parent;
167167
tp->q = q;
168-
tp->block = block;
168+
tp->chain = chain;
169169

170170
err = tp->ops->init(tp);
171171
if (err) {
@@ -186,22 +186,57 @@ static void tcf_proto_destroy(struct tcf_proto *tp)
186186
kfree_rcu(tp, rcu);
187187
}
188188

189-
static struct tcf_chain *tcf_chain_create(void)
189+
static struct tcf_chain *tcf_chain_create(struct tcf_block *block,
190+
u32 chain_index)
190191
{
191-
return kzalloc(sizeof(struct tcf_chain), GFP_KERNEL);
192+
struct tcf_chain *chain;
193+
194+
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
195+
if (!chain)
196+
return NULL;
197+
list_add_tail(&chain->list, &block->chain_list);
198+
chain->block = block;
199+
chain->index = chain_index;
200+
chain->refcnt = 1;
201+
return chain;
192202
}
193203

194204
static void tcf_chain_destroy(struct tcf_chain *chain)
195205
{
196206
struct tcf_proto *tp;
197207

208+
list_del(&chain->list);
198209
while ((tp = rtnl_dereference(chain->filter_chain)) != NULL) {
199210
RCU_INIT_POINTER(chain->filter_chain, tp->next);
200211
tcf_proto_destroy(tp);
201212
}
202213
kfree(chain);
203214
}
204215

216+
struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index)
217+
{
218+
struct tcf_chain *chain;
219+
220+
list_for_each_entry(chain, &block->chain_list, list) {
221+
if (chain->index == chain_index) {
222+
chain->refcnt++;
223+
return chain;
224+
}
225+
}
226+
return tcf_chain_create(block, chain_index);
227+
}
228+
EXPORT_SYMBOL(tcf_chain_get);
229+
230+
void tcf_chain_put(struct tcf_chain *chain)
231+
{
232+
/* Destroy unused chain, with exception of chain 0, which is the
233+
* default one and has to be always present.
234+
*/
235+
if (--chain->refcnt == 0 && !chain->filter_chain && chain->index != 0)
236+
tcf_chain_destroy(chain);
237+
}
238+
EXPORT_SYMBOL(tcf_chain_put);
239+
205240
static void
206241
tcf_chain_filter_chain_ptr_set(struct tcf_chain *chain,
207242
struct tcf_proto __rcu **p_filter_chain)
@@ -213,16 +248,19 @@ int tcf_block_get(struct tcf_block **p_block,
213248
struct tcf_proto __rcu **p_filter_chain)
214249
{
215250
struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL);
251+
struct tcf_chain *chain;
216252
int err;
217253

218254
if (!block)
219255
return -ENOMEM;
220-
block->chain = tcf_chain_create();
221-
if (!block->chain) {
256+
INIT_LIST_HEAD(&block->chain_list);
257+
/* Create chain 0 by default, it has to be always present. */
258+
chain = tcf_chain_create(block, 0);
259+
if (!chain) {
222260
err = -ENOMEM;
223261
goto err_chain_create;
224262
}
225-
tcf_chain_filter_chain_ptr_set(block->chain, p_filter_chain);
263+
tcf_chain_filter_chain_ptr_set(chain, p_filter_chain);
226264
*p_block = block;
227265
return 0;
228266

@@ -234,9 +272,13 @@ EXPORT_SYMBOL(tcf_block_get);
234272

235273
void tcf_block_put(struct tcf_block *block)
236274
{
275+
struct tcf_chain *chain, *tmp;
276+
237277
if (!block)
238278
return;
239-
tcf_chain_destroy(block->chain);
279+
280+
list_for_each_entry_safe(chain, tmp, &block->chain_list, list)
281+
tcf_chain_destroy(chain);
240282
kfree(block);
241283
}
242284
EXPORT_SYMBOL(tcf_block_put);
@@ -360,10 +402,11 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
360402
u32 prio;
361403
bool prio_allocate;
362404
u32 parent;
405+
u32 chain_index;
363406
struct net_device *dev;
364407
struct Qdisc *q;
365408
struct tcf_chain_info chain_info;
366-
struct tcf_chain *chain;
409+
struct tcf_chain *chain = NULL;
367410
struct tcf_block *block;
368411
struct tcf_proto *tp;
369412
const struct Qdisc_class_ops *cops;
@@ -449,7 +492,17 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
449492
err = -EINVAL;
450493
goto errout;
451494
}
452-
chain = block->chain;
495+
496+
chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
497+
if (chain_index > TC_ACT_EXT_VAL_MASK) {
498+
err = -EINVAL;
499+
goto errout;
500+
}
501+
chain = tcf_chain_get(block, chain_index);
502+
if (!chain) {
503+
err = -ENOMEM;
504+
goto errout;
505+
}
453506

454507
if (n->nlmsg_type == RTM_DELTFILTER && prio == 0) {
455508
tfilter_notify_chain(net, skb, n, chain, RTM_DELTFILTER);
@@ -483,7 +536,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
483536
prio = tcf_auto_prio(tcf_chain_tp_prev(&chain_info));
484537

485538
tp = tcf_proto_create(nla_data(tca[TCA_KIND]),
486-
protocol, prio, parent, q, block);
539+
protocol, prio, parent, q, chain);
487540
if (IS_ERR(tp)) {
488541
err = PTR_ERR(tp);
489542
goto errout;
@@ -556,6 +609,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
556609
}
557610

558611
errout:
612+
if (chain)
613+
tcf_chain_put(chain);
559614
if (cl)
560615
cops->put(q, cl);
561616
if (err == -EAGAIN)
@@ -584,6 +639,8 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb,
584639
tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol);
585640
if (nla_put_string(skb, TCA_KIND, tp->ops->kind))
586641
goto nla_put_failure;
642+
if (nla_put_u32(skb, TCA_CHAIN, tp->chain->index))
643+
goto nla_put_failure;
587644
tcm->tcm_handle = fh;
588645
if (RTM_DELTFILTER != event) {
589646
tcm->tcm_handle = 0;
@@ -640,7 +697,7 @@ static int tcf_node_dump(struct tcf_proto *tp, unsigned long n,
640697
RTM_NEWTFILTER);
641698
}
642699

643-
static void tcf_chain_dump(struct tcf_chain *chain, struct sk_buff *skb,
700+
static bool tcf_chain_dump(struct tcf_chain *chain, struct sk_buff *skb,
644701
struct netlink_callback *cb,
645702
long index_start, long *p_index)
646703
{
@@ -667,7 +724,7 @@ static void tcf_chain_dump(struct tcf_chain *chain, struct sk_buff *skb,
667724
NETLINK_CB(cb->skb).portid,
668725
cb->nlh->nlmsg_seq, NLM_F_MULTI,
669726
RTM_NEWTFILTER) <= 0)
670-
break;
727+
return false;
671728

672729
cb->args[1] = 1;
673730
}
@@ -682,14 +739,16 @@ static void tcf_chain_dump(struct tcf_chain *chain, struct sk_buff *skb,
682739
tp->ops->walk(tp, &arg.w);
683740
cb->args[1] = arg.w.count + 1;
684741
if (arg.w.stop)
685-
break;
742+
return false;
686743
}
744+
return true;
687745
}
688746

689747
/* called with RTNL */
690748
static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
691749
{
692750
struct net *net = sock_net(skb->sk);
751+
struct nlattr *tca[TCA_MAX + 1];
693752
struct net_device *dev;
694753
struct Qdisc *q;
695754
struct tcf_block *block;
@@ -699,9 +758,15 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
699758
const struct Qdisc_class_ops *cops;
700759
long index_start;
701760
long index;
761+
int err;
702762

703763
if (nlmsg_len(cb->nlh) < sizeof(*tcm))
704764
return skb->len;
765+
766+
err = nlmsg_parse(cb->nlh, sizeof(*tcm), tca, TCA_MAX, NULL, NULL);
767+
if (err)
768+
return err;
769+
705770
dev = __dev_get_by_index(net, tcm->tcm_ifindex);
706771
if (!dev)
707772
return skb->len;
@@ -725,11 +790,18 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
725790
block = cops->tcf_block(q, cl);
726791
if (!block)
727792
goto errout;
728-
chain = block->chain;
729793

730794
index_start = cb->args[0];
731795
index = 0;
732-
tcf_chain_dump(chain, skb, cb, index_start, &index);
796+
797+
list_for_each_entry(chain, &block->chain_list, list) {
798+
if (tca[TCA_CHAIN] &&
799+
nla_get_u32(tca[TCA_CHAIN]) != chain->index)
800+
continue;
801+
if (!tcf_chain_dump(chain, skb, cb, index_start, &index))
802+
break;
803+
}
804+
733805
cb->args[0] = index;
734806

735807
errout:

0 commit comments

Comments
 (0)