Commit 9888faef authored by John Fastabend's avatar John Fastabend Committed by David S. Miller

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: default avatarJohn Fastabend <john.r.fastabend@intel.com>
Acked-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 25d8c0d5
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
struct basic_head { struct basic_head {
u32 hgenerator; u32 hgenerator;
struct list_head flist; struct list_head flist;
struct rcu_head rcu;
}; };
struct basic_filter { struct basic_filter {
...@@ -31,17 +32,19 @@ struct basic_filter { ...@@ -31,17 +32,19 @@ struct basic_filter {
struct tcf_exts exts; struct tcf_exts exts;
struct tcf_ematch_tree ematches; struct tcf_ematch_tree ematches;
struct tcf_result res; struct tcf_result res;
struct tcf_proto *tp;
struct list_head link; struct list_head link;
struct rcu_head rcu;
}; };
static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp, static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp,
struct tcf_result *res) struct tcf_result *res)
{ {
int r; int r;
struct basic_head *head = tp->root; struct basic_head *head = rcu_dereference_bh(tp->root);
struct basic_filter *f; struct basic_filter *f;
list_for_each_entry(f, &head->flist, link) { list_for_each_entry_rcu(f, &head->flist, link) {
if (!tcf_em_tree_match(skb, &f->ematches, NULL)) if (!tcf_em_tree_match(skb, &f->ematches, NULL))
continue; continue;
*res = f->res; *res = f->res;
...@@ -56,7 +59,7 @@ static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp, ...@@ -56,7 +59,7 @@ static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp,
static unsigned long basic_get(struct tcf_proto *tp, u32 handle) static unsigned long basic_get(struct tcf_proto *tp, u32 handle)
{ {
unsigned long l = 0UL; unsigned long l = 0UL;
struct basic_head *head = tp->root; struct basic_head *head = rtnl_dereference(tp->root);
struct basic_filter *f; struct basic_filter *f;
if (head == NULL) if (head == NULL)
...@@ -81,12 +84,15 @@ static int basic_init(struct tcf_proto *tp) ...@@ -81,12 +84,15 @@ static int basic_init(struct tcf_proto *tp)
if (head == NULL) if (head == NULL)
return -ENOBUFS; return -ENOBUFS;
INIT_LIST_HEAD(&head->flist); INIT_LIST_HEAD(&head->flist);
tp->root = head; rcu_assign_pointer(tp->root, head);
return 0; return 0;
} }
static void basic_delete_filter(struct tcf_proto *tp, struct basic_filter *f) static void basic_delete_filter(struct rcu_head *head)
{ {
struct basic_filter *f = container_of(head, struct basic_filter, rcu);
struct tcf_proto *tp = f->tp;
tcf_unbind_filter(tp, &f->res); tcf_unbind_filter(tp, &f->res);
tcf_exts_destroy(tp, &f->exts); tcf_exts_destroy(tp, &f->exts);
tcf_em_tree_destroy(tp, &f->ematches); tcf_em_tree_destroy(tp, &f->ematches);
...@@ -95,27 +101,26 @@ static void basic_delete_filter(struct tcf_proto *tp, struct basic_filter *f) ...@@ -95,27 +101,26 @@ static void basic_delete_filter(struct tcf_proto *tp, struct basic_filter *f)
static void basic_destroy(struct tcf_proto *tp) static void basic_destroy(struct tcf_proto *tp)
{ {
struct basic_head *head = tp->root; struct basic_head *head = rtnl_dereference(tp->root);
struct basic_filter *f, *n; struct basic_filter *f, *n;
list_for_each_entry_safe(f, n, &head->flist, link) { list_for_each_entry_safe(f, n, &head->flist, link) {
list_del(&f->link); list_del_rcu(&f->link);
basic_delete_filter(tp, f); call_rcu(&f->rcu, basic_delete_filter);
} }
kfree(head); RCU_INIT_POINTER(tp->root, NULL);
kfree_rcu(head, rcu);
} }
static int basic_delete(struct tcf_proto *tp, unsigned long arg) static int basic_delete(struct tcf_proto *tp, unsigned long arg)
{ {
struct basic_head *head = tp->root; struct basic_head *head = rtnl_dereference(tp->root);
struct basic_filter *t, *f = (struct basic_filter *) arg; struct basic_filter *t, *f = (struct basic_filter *) arg;
list_for_each_entry(t, &head->flist, link) list_for_each_entry(t, &head->flist, link)
if (t == f) { if (t == f) {
tcf_tree_lock(tp); list_del_rcu(&t->link);
list_del(&t->link); call_rcu(&t->rcu, basic_delete_filter);
tcf_tree_unlock(tp);
basic_delete_filter(tp, t);
return 0; return 0;
} }
...@@ -152,6 +157,7 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp, ...@@ -152,6 +157,7 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp,
tcf_exts_change(tp, &f->exts, &e); tcf_exts_change(tp, &f->exts, &e);
tcf_em_tree_change(tp, &f->ematches, &t); tcf_em_tree_change(tp, &f->ematches, &t);
f->tp = tp;
return 0; return 0;
errout: errout:
...@@ -164,9 +170,10 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, ...@@ -164,9 +170,10 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
struct nlattr **tca, unsigned long *arg, bool ovr) struct nlattr **tca, unsigned long *arg, bool ovr)
{ {
int err; int err;
struct basic_head *head = tp->root; struct basic_head *head = rtnl_dereference(tp->root);
struct nlattr *tb[TCA_BASIC_MAX + 1]; struct nlattr *tb[TCA_BASIC_MAX + 1];
struct basic_filter *f = (struct basic_filter *) *arg; struct basic_filter *fold = (struct basic_filter *) *arg;
struct basic_filter *fnew;
if (tca[TCA_OPTIONS] == NULL) if (tca[TCA_OPTIONS] == NULL)
return -EINVAL; return -EINVAL;
...@@ -176,22 +183,23 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, ...@@ -176,22 +183,23 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
if (err < 0) if (err < 0)
return err; return err;
if (f != NULL) { if (fold != NULL) {
if (handle && f->handle != handle) if (handle && fold->handle != handle)
return -EINVAL; return -EINVAL;
return basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE], ovr);
} }
err = -ENOBUFS; err = -ENOBUFS;
f = kzalloc(sizeof(*f), GFP_KERNEL); fnew = kzalloc(sizeof(*fnew), GFP_KERNEL);
if (f == NULL) if (fnew == NULL)
goto errout; goto errout;
tcf_exts_init(&f->exts, TCA_BASIC_ACT, TCA_BASIC_POLICE); tcf_exts_init(&fnew->exts, TCA_BASIC_ACT, TCA_BASIC_POLICE);
err = -EINVAL; err = -EINVAL;
if (handle) if (handle) {
f->handle = handle; fnew->handle = handle;
else { } else if (fold) {
fnew->handle = fold->handle;
} else {
unsigned int i = 0x80000000; unsigned int i = 0x80000000;
do { do {
if (++head->hgenerator == 0x7FFFFFFF) if (++head->hgenerator == 0x7FFFFFFF)
...@@ -203,29 +211,31 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, ...@@ -203,29 +211,31 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
goto errout; goto errout;
} }
f->handle = head->hgenerator; fnew->handle = head->hgenerator;
} }
err = basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE], ovr); err = basic_set_parms(net, tp, fnew, base, tb, tca[TCA_RATE], ovr);
if (err < 0) if (err < 0)
goto errout; goto errout;
tcf_tree_lock(tp); *arg = (unsigned long)fnew;
list_add(&f->link, &head->flist);
tcf_tree_unlock(tp); if (fold) {
*arg = (unsigned long) f; list_replace_rcu(&fold->link, &fnew->link);
call_rcu(&fold->rcu, basic_delete_filter);
} else {
list_add_rcu(&fnew->link, &head->flist);
}
return 0; return 0;
errout: errout:
if (*arg == 0UL && f) kfree(fnew);
kfree(f);
return err; return err;
} }
static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg) static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg)
{ {
struct basic_head *head = tp->root; struct basic_head *head = rtnl_dereference(tp->root);
struct basic_filter *f; struct basic_filter *f;
list_for_each_entry(f, &head->flist, link) { list_for_each_entry(f, &head->flist, link) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment