Commit 202e651c authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso

netfilter: conntrack: gre: convert rwlock to rcu

We can use gre.  Lock is only needed when a new expectation is added.

In case a single spinlock proves to be problematic we can either add one
per netns or use an array of locks combined with net_hash_mix() or similar
to pick the 'correct' one.

But given this is only needed for an expectation rather than per packet
a single one should be ok.
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent e2e48b47
...@@ -19,6 +19,7 @@ struct nf_conn; ...@@ -19,6 +19,7 @@ struct nf_conn;
struct nf_ct_gre_keymap { struct nf_ct_gre_keymap {
struct list_head list; struct list_head list;
struct nf_conntrack_tuple tuple; struct nf_conntrack_tuple tuple;
struct rcu_head rcu;
}; };
enum grep_conntrack { enum grep_conntrack {
......
...@@ -49,6 +49,8 @@ static const unsigned int gre_timeouts[GRE_CT_MAX] = { ...@@ -49,6 +49,8 @@ static const unsigned int gre_timeouts[GRE_CT_MAX] = {
}; };
static unsigned int proto_gre_net_id __read_mostly; static unsigned int proto_gre_net_id __read_mostly;
/* used when expectation is added */
static DEFINE_SPINLOCK(keymap_lock);
static inline struct netns_proto_gre *gre_pernet(struct net *net) static inline struct netns_proto_gre *gre_pernet(struct net *net)
{ {
...@@ -60,12 +62,12 @@ static void nf_ct_gre_keymap_flush(struct net *net) ...@@ -60,12 +62,12 @@ static void nf_ct_gre_keymap_flush(struct net *net)
struct netns_proto_gre *net_gre = gre_pernet(net); struct netns_proto_gre *net_gre = gre_pernet(net);
struct nf_ct_gre_keymap *km, *tmp; struct nf_ct_gre_keymap *km, *tmp;
write_lock_bh(&net_gre->keymap_lock); spin_lock_bh(&keymap_lock);
list_for_each_entry_safe(km, tmp, &net_gre->keymap_list, list) { list_for_each_entry_safe(km, tmp, &net_gre->keymap_list, list) {
list_del(&km->list); list_del_rcu(&km->list);
kfree(km); kfree_rcu(km, rcu);
} }
write_unlock_bh(&net_gre->keymap_lock); spin_unlock_bh(&keymap_lock);
} }
static inline int gre_key_cmpfn(const struct nf_ct_gre_keymap *km, static inline int gre_key_cmpfn(const struct nf_ct_gre_keymap *km,
...@@ -85,14 +87,12 @@ static __be16 gre_keymap_lookup(struct net *net, struct nf_conntrack_tuple *t) ...@@ -85,14 +87,12 @@ static __be16 gre_keymap_lookup(struct net *net, struct nf_conntrack_tuple *t)
struct nf_ct_gre_keymap *km; struct nf_ct_gre_keymap *km;
__be16 key = 0; __be16 key = 0;
read_lock_bh(&net_gre->keymap_lock); list_for_each_entry_rcu(km, &net_gre->keymap_list, list) {
list_for_each_entry(km, &net_gre->keymap_list, list) {
if (gre_key_cmpfn(km, t)) { if (gre_key_cmpfn(km, t)) {
key = km->tuple.src.u.gre.key; key = km->tuple.src.u.gre.key;
break; break;
} }
} }
read_unlock_bh(&net_gre->keymap_lock);
pr_debug("lookup src key 0x%x for ", key); pr_debug("lookup src key 0x%x for ", key);
nf_ct_dump_tuple(t); nf_ct_dump_tuple(t);
...@@ -112,14 +112,10 @@ int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, ...@@ -112,14 +112,10 @@ int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
kmp = &ct_pptp_info->keymap[dir]; kmp = &ct_pptp_info->keymap[dir];
if (*kmp) { if (*kmp) {
/* check whether it's a retransmission */ /* check whether it's a retransmission */
read_lock_bh(&net_gre->keymap_lock); list_for_each_entry_rcu(km, &net_gre->keymap_list, list) {
list_for_each_entry(km, &net_gre->keymap_list, list) { if (gre_key_cmpfn(km, t) && km == *kmp)
if (gre_key_cmpfn(km, t) && km == *kmp) {
read_unlock_bh(&net_gre->keymap_lock);
return 0; return 0;
}
} }
read_unlock_bh(&net_gre->keymap_lock);
pr_debug("trying to override keymap_%s for ct %p\n", pr_debug("trying to override keymap_%s for ct %p\n",
dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct); dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct);
return -EEXIST; return -EEXIST;
...@@ -134,9 +130,9 @@ int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, ...@@ -134,9 +130,9 @@ int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
pr_debug("adding new entry %p: ", km); pr_debug("adding new entry %p: ", km);
nf_ct_dump_tuple(&km->tuple); nf_ct_dump_tuple(&km->tuple);
write_lock_bh(&net_gre->keymap_lock); spin_lock_bh(&keymap_lock);
list_add_tail(&km->list, &net_gre->keymap_list); list_add_tail(&km->list, &net_gre->keymap_list);
write_unlock_bh(&net_gre->keymap_lock); spin_unlock_bh(&keymap_lock);
return 0; return 0;
} }
...@@ -145,24 +141,22 @@ EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add); ...@@ -145,24 +141,22 @@ EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add);
/* destroy the keymap entries associated with specified master ct */ /* destroy the keymap entries associated with specified master ct */
void nf_ct_gre_keymap_destroy(struct nf_conn *ct) void nf_ct_gre_keymap_destroy(struct nf_conn *ct)
{ {
struct net *net = nf_ct_net(ct);
struct netns_proto_gre *net_gre = gre_pernet(net);
struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct); struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
enum ip_conntrack_dir dir; enum ip_conntrack_dir dir;
pr_debug("entering for ct %p\n", ct); pr_debug("entering for ct %p\n", ct);
write_lock_bh(&net_gre->keymap_lock); spin_lock_bh(&keymap_lock);
for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) { for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) {
if (ct_pptp_info->keymap[dir]) { if (ct_pptp_info->keymap[dir]) {
pr_debug("removing %p from list\n", pr_debug("removing %p from list\n",
ct_pptp_info->keymap[dir]); ct_pptp_info->keymap[dir]);
list_del(&ct_pptp_info->keymap[dir]->list); list_del_rcu(&ct_pptp_info->keymap[dir]->list);
kfree(ct_pptp_info->keymap[dir]); kfree_rcu(ct_pptp_info->keymap[dir], rcu);
ct_pptp_info->keymap[dir] = NULL; ct_pptp_info->keymap[dir] = NULL;
} }
} }
write_unlock_bh(&net_gre->keymap_lock); spin_unlock_bh(&keymap_lock);
} }
EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_destroy); EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_destroy);
...@@ -365,7 +359,6 @@ static int gre_init_net(struct net *net) ...@@ -365,7 +359,6 @@ static int gre_init_net(struct net *net)
struct nf_proto_net *nf = &net_gre->nf; struct nf_proto_net *nf = &net_gre->nf;
int i; int i;
rwlock_init(&net_gre->keymap_lock);
INIT_LIST_HEAD(&net_gre->keymap_list); INIT_LIST_HEAD(&net_gre->keymap_list);
for (i = 0; i < GRE_CT_MAX; i++) for (i = 0; i < GRE_CT_MAX; i++)
net_gre->gre_timeouts[i] = gre_timeouts[i]; net_gre->gre_timeouts[i] = gre_timeouts[i];
......
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