Commit 76cf546c authored by Cong Wang's avatar Cong Wang Committed by David S. Miller

net_sched: use idr to allocate bpf filter handles

Instead of calling cls_bpf_get() in a loop to find
a unused handle, just switch to idr API to allocate
new handles.

Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Chris Mi <chrism@mellanox.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: default avatarCong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8f1975e3
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/filter.h> #include <linux/filter.h>
#include <linux/bpf.h> #include <linux/bpf.h>
#include <linux/idr.h>
#include <net/rtnetlink.h> #include <net/rtnetlink.h>
#include <net/pkt_cls.h> #include <net/pkt_cls.h>
...@@ -32,7 +33,7 @@ MODULE_DESCRIPTION("TC BPF based classifier"); ...@@ -32,7 +33,7 @@ MODULE_DESCRIPTION("TC BPF based classifier");
struct cls_bpf_head { struct cls_bpf_head {
struct list_head plist; struct list_head plist;
u32 hgen; struct idr handle_idr;
struct rcu_head rcu; struct rcu_head rcu;
}; };
...@@ -238,6 +239,7 @@ static int cls_bpf_init(struct tcf_proto *tp) ...@@ -238,6 +239,7 @@ static int cls_bpf_init(struct tcf_proto *tp)
return -ENOBUFS; return -ENOBUFS;
INIT_LIST_HEAD_RCU(&head->plist); INIT_LIST_HEAD_RCU(&head->plist);
idr_init(&head->handle_idr);
rcu_assign_pointer(tp->root, head); rcu_assign_pointer(tp->root, head);
return 0; return 0;
...@@ -264,6 +266,9 @@ static void cls_bpf_delete_prog_rcu(struct rcu_head *rcu) ...@@ -264,6 +266,9 @@ static void cls_bpf_delete_prog_rcu(struct rcu_head *rcu)
static void __cls_bpf_delete(struct tcf_proto *tp, struct cls_bpf_prog *prog) static void __cls_bpf_delete(struct tcf_proto *tp, struct cls_bpf_prog *prog)
{ {
struct cls_bpf_head *head = rtnl_dereference(tp->root);
idr_remove_ext(&head->handle_idr, prog->handle);
cls_bpf_stop_offload(tp, prog); cls_bpf_stop_offload(tp, prog);
list_del_rcu(&prog->link); list_del_rcu(&prog->link);
tcf_unbind_filter(tp, &prog->res); tcf_unbind_filter(tp, &prog->res);
...@@ -287,6 +292,7 @@ static void cls_bpf_destroy(struct tcf_proto *tp) ...@@ -287,6 +292,7 @@ static void cls_bpf_destroy(struct tcf_proto *tp)
list_for_each_entry_safe(prog, tmp, &head->plist, link) list_for_each_entry_safe(prog, tmp, &head->plist, link)
__cls_bpf_delete(tp, prog); __cls_bpf_delete(tp, prog);
idr_destroy(&head->handle_idr);
kfree_rcu(head, rcu); kfree_rcu(head, rcu);
} }
...@@ -421,27 +427,6 @@ static int cls_bpf_set_parms(struct net *net, struct tcf_proto *tp, ...@@ -421,27 +427,6 @@ static int cls_bpf_set_parms(struct net *net, struct tcf_proto *tp,
return 0; return 0;
} }
static u32 cls_bpf_grab_new_handle(struct tcf_proto *tp,
struct cls_bpf_head *head)
{
unsigned int i = 0x80000000;
u32 handle;
do {
if (++head->hgen == 0x7FFFFFFF)
head->hgen = 1;
} while (--i > 0 && cls_bpf_get(tp, head->hgen));
if (unlikely(i == 0)) {
pr_err("Insufficient number of handles\n");
handle = 0;
} else {
handle = head->hgen;
}
return handle;
}
static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
struct tcf_proto *tp, unsigned long base, struct tcf_proto *tp, unsigned long base,
u32 handle, struct nlattr **tca, u32 handle, struct nlattr **tca,
...@@ -451,6 +436,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, ...@@ -451,6 +436,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
struct cls_bpf_prog *oldprog = *arg; struct cls_bpf_prog *oldprog = *arg;
struct nlattr *tb[TCA_BPF_MAX + 1]; struct nlattr *tb[TCA_BPF_MAX + 1];
struct cls_bpf_prog *prog; struct cls_bpf_prog *prog;
unsigned long idr_index;
int ret; int ret;
if (tca[TCA_OPTIONS] == NULL) if (tca[TCA_OPTIONS] == NULL)
...@@ -476,21 +462,30 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, ...@@ -476,21 +462,30 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
} }
} }
if (handle == 0) if (handle == 0) {
prog->handle = cls_bpf_grab_new_handle(tp, head); ret = idr_alloc_ext(&head->handle_idr, prog, &idr_index,
else 1, 0x7FFFFFFF, GFP_KERNEL);
prog->handle = handle; if (ret)
if (prog->handle == 0) { goto errout;
ret = -EINVAL; prog->handle = idr_index;
} else {
if (!oldprog) {
ret = idr_alloc_ext(&head->handle_idr, prog, &idr_index,
handle, handle + 1, GFP_KERNEL);
if (ret)
goto errout; goto errout;
} }
prog->handle = handle;
}
ret = cls_bpf_set_parms(net, tp, prog, base, tb, tca[TCA_RATE], ovr); ret = cls_bpf_set_parms(net, tp, prog, base, tb, tca[TCA_RATE], ovr);
if (ret < 0) if (ret < 0)
goto errout; goto errout_idr;
ret = cls_bpf_offload(tp, prog, oldprog); ret = cls_bpf_offload(tp, prog, oldprog);
if (ret) { if (ret) {
if (!oldprog)
idr_remove_ext(&head->handle_idr, prog->handle);
__cls_bpf_delete_prog(prog); __cls_bpf_delete_prog(prog);
return ret; return ret;
} }
...@@ -499,6 +494,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, ...@@ -499,6 +494,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
prog->gen_flags |= TCA_CLS_FLAGS_NOT_IN_HW; prog->gen_flags |= TCA_CLS_FLAGS_NOT_IN_HW;
if (oldprog) { if (oldprog) {
idr_replace_ext(&head->handle_idr, prog, handle);
list_replace_rcu(&oldprog->link, &prog->link); list_replace_rcu(&oldprog->link, &prog->link);
tcf_unbind_filter(tp, &oldprog->res); tcf_unbind_filter(tp, &oldprog->res);
call_rcu(&oldprog->rcu, cls_bpf_delete_prog_rcu); call_rcu(&oldprog->rcu, cls_bpf_delete_prog_rcu);
...@@ -509,6 +505,9 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, ...@@ -509,6 +505,9 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
*arg = prog; *arg = prog;
return 0; return 0;
errout_idr:
if (!oldprog)
idr_remove_ext(&head->handle_idr, prog->handle);
errout: errout:
tcf_exts_destroy(&prog->exts); tcf_exts_destroy(&prog->exts);
kfree(prog); kfree(prog);
......
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