Skip to content

Commit 9888fae

Browse files
jrfastabdavem330
authored andcommitted
net: sched: cls_basic use RCU
Enable basic classifier for RCU. Dereferencing tp->root may look a bit strange here but it is needed by my accounting because it is allocated at init time and needs to be kfree'd at destroy time. However because it may be referenced in the classify() path we must wait an RCU grace period before free'ing it. We use kfree_rcu() and rcu_ APIs to enforce this. This pattern is used in all the classifiers. Also the hgenerator can be incremented without concern because it is always incremented under RTNL. Signed-off-by: John Fastabend <john.r.fastabend@intel.com> Acked-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 25d8c0d commit 9888fae

File tree

1 file changed

+45
-35
lines changed

1 file changed

+45
-35
lines changed

net/sched/cls_basic.c

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,27 @@
2424
struct basic_head {
2525
u32 hgenerator;
2626
struct list_head flist;
27+
struct rcu_head rcu;
2728
};
2829

2930
struct basic_filter {
3031
u32 handle;
3132
struct tcf_exts exts;
3233
struct tcf_ematch_tree ematches;
3334
struct tcf_result res;
35+
struct tcf_proto *tp;
3436
struct list_head link;
37+
struct rcu_head rcu;
3538
};
3639

3740
static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp,
3841
struct tcf_result *res)
3942
{
4043
int r;
41-
struct basic_head *head = tp->root;
44+
struct basic_head *head = rcu_dereference_bh(tp->root);
4245
struct basic_filter *f;
4346

44-
list_for_each_entry(f, &head->flist, link) {
47+
list_for_each_entry_rcu(f, &head->flist, link) {
4548
if (!tcf_em_tree_match(skb, &f->ematches, NULL))
4649
continue;
4750
*res = f->res;
@@ -56,7 +59,7 @@ static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp,
5659
static unsigned long basic_get(struct tcf_proto *tp, u32 handle)
5760
{
5861
unsigned long l = 0UL;
59-
struct basic_head *head = tp->root;
62+
struct basic_head *head = rtnl_dereference(tp->root);
6063
struct basic_filter *f;
6164

6265
if (head == NULL)
@@ -81,12 +84,15 @@ static int basic_init(struct tcf_proto *tp)
8184
if (head == NULL)
8285
return -ENOBUFS;
8386
INIT_LIST_HEAD(&head->flist);
84-
tp->root = head;
87+
rcu_assign_pointer(tp->root, head);
8588
return 0;
8689
}
8790

88-
static void basic_delete_filter(struct tcf_proto *tp, struct basic_filter *f)
91+
static void basic_delete_filter(struct rcu_head *head)
8992
{
93+
struct basic_filter *f = container_of(head, struct basic_filter, rcu);
94+
struct tcf_proto *tp = f->tp;
95+
9096
tcf_unbind_filter(tp, &f->res);
9197
tcf_exts_destroy(tp, &f->exts);
9298
tcf_em_tree_destroy(tp, &f->ematches);
@@ -95,27 +101,26 @@ static void basic_delete_filter(struct tcf_proto *tp, struct basic_filter *f)
95101

96102
static void basic_destroy(struct tcf_proto *tp)
97103
{
98-
struct basic_head *head = tp->root;
104+
struct basic_head *head = rtnl_dereference(tp->root);
99105
struct basic_filter *f, *n;
100106

101107
list_for_each_entry_safe(f, n, &head->flist, link) {
102-
list_del(&f->link);
103-
basic_delete_filter(tp, f);
108+
list_del_rcu(&f->link);
109+
call_rcu(&f->rcu, basic_delete_filter);
104110
}
105-
kfree(head);
111+
RCU_INIT_POINTER(tp->root, NULL);
112+
kfree_rcu(head, rcu);
106113
}
107114

108115
static int basic_delete(struct tcf_proto *tp, unsigned long arg)
109116
{
110-
struct basic_head *head = tp->root;
117+
struct basic_head *head = rtnl_dereference(tp->root);
111118
struct basic_filter *t, *f = (struct basic_filter *) arg;
112119

113120
list_for_each_entry(t, &head->flist, link)
114121
if (t == f) {
115-
tcf_tree_lock(tp);
116-
list_del(&t->link);
117-
tcf_tree_unlock(tp);
118-
basic_delete_filter(tp, t);
122+
list_del_rcu(&t->link);
123+
call_rcu(&t->rcu, basic_delete_filter);
119124
return 0;
120125
}
121126

@@ -152,6 +157,7 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp,
152157

153158
tcf_exts_change(tp, &f->exts, &e);
154159
tcf_em_tree_change(tp, &f->ematches, &t);
160+
f->tp = tp;
155161

156162
return 0;
157163
errout:
@@ -164,9 +170,10 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
164170
struct nlattr **tca, unsigned long *arg, bool ovr)
165171
{
166172
int err;
167-
struct basic_head *head = tp->root;
173+
struct basic_head *head = rtnl_dereference(tp->root);
168174
struct nlattr *tb[TCA_BASIC_MAX + 1];
169-
struct basic_filter *f = (struct basic_filter *) *arg;
175+
struct basic_filter *fold = (struct basic_filter *) *arg;
176+
struct basic_filter *fnew;
170177

171178
if (tca[TCA_OPTIONS] == NULL)
172179
return -EINVAL;
@@ -176,22 +183,23 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
176183
if (err < 0)
177184
return err;
178185

179-
if (f != NULL) {
180-
if (handle && f->handle != handle)
186+
if (fold != NULL) {
187+
if (handle && fold->handle != handle)
181188
return -EINVAL;
182-
return basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE], ovr);
183189
}
184190

185191
err = -ENOBUFS;
186-
f = kzalloc(sizeof(*f), GFP_KERNEL);
187-
if (f == NULL)
192+
fnew = kzalloc(sizeof(*fnew), GFP_KERNEL);
193+
if (fnew == NULL)
188194
goto errout;
189195

190-
tcf_exts_init(&f->exts, TCA_BASIC_ACT, TCA_BASIC_POLICE);
196+
tcf_exts_init(&fnew->exts, TCA_BASIC_ACT, TCA_BASIC_POLICE);
191197
err = -EINVAL;
192-
if (handle)
193-
f->handle = handle;
194-
else {
198+
if (handle) {
199+
fnew->handle = handle;
200+
} else if (fold) {
201+
fnew->handle = fold->handle;
202+
} else {
195203
unsigned int i = 0x80000000;
196204
do {
197205
if (++head->hgenerator == 0x7FFFFFFF)
@@ -203,29 +211,31 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
203211
goto errout;
204212
}
205213

206-
f->handle = head->hgenerator;
214+
fnew->handle = head->hgenerator;
207215
}
208216

209-
err = basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE], ovr);
217+
err = basic_set_parms(net, tp, fnew, base, tb, tca[TCA_RATE], ovr);
210218
if (err < 0)
211219
goto errout;
212220

213-
tcf_tree_lock(tp);
214-
list_add(&f->link, &head->flist);
215-
tcf_tree_unlock(tp);
216-
*arg = (unsigned long) f;
221+
*arg = (unsigned long)fnew;
222+
223+
if (fold) {
224+
list_replace_rcu(&fold->link, &fnew->link);
225+
call_rcu(&fold->rcu, basic_delete_filter);
226+
} else {
227+
list_add_rcu(&fnew->link, &head->flist);
228+
}
217229

218230
return 0;
219231
errout:
220-
if (*arg == 0UL && f)
221-
kfree(f);
222-
232+
kfree(fnew);
223233
return err;
224234
}
225235

226236
static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg)
227237
{
228-
struct basic_head *head = tp->root;
238+
struct basic_head *head = rtnl_dereference(tp->root);
229239
struct basic_filter *f;
230240

231241
list_for_each_entry(f, &head->flist, link) {

0 commit comments

Comments
 (0)