Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
202e651c e2e48b47

+16 -22
+1
include/linux/netfilter/nf_conntrack_proto_gre.h
··· 19 19 struct nf_ct_gre_keymap { 20 20 struct list_head list; 21 21 struct nf_conntrack_tuple tuple; 22 + struct rcu_head rcu; 22 23 }; 23 24 24 25 enum grep_conntrack {
+15 -22
net/netfilter/nf_conntrack_proto_gre.c
··· 49 49 }; 50 50 51 51 static unsigned int proto_gre_net_id __read_mostly; 52 + /* used when expectation is added */ 53 + static DEFINE_SPINLOCK(keymap_lock); 52 54 53 55 static inline struct netns_proto_gre *gre_pernet(struct net *net) 54 56 { ··· 62 60 struct netns_proto_gre *net_gre = gre_pernet(net); 63 61 struct nf_ct_gre_keymap *km, *tmp; 64 62 65 - write_lock_bh(&net_gre->keymap_lock); 63 + spin_lock_bh(&keymap_lock); 66 64 list_for_each_entry_safe(km, tmp, &net_gre->keymap_list, list) { 67 - list_del(&km->list); 68 - kfree(km); 65 + list_del_rcu(&km->list); 66 + kfree_rcu(km, rcu); 69 67 } 70 - write_unlock_bh(&net_gre->keymap_lock); 68 + spin_unlock_bh(&keymap_lock); 71 69 } 72 70 73 71 static inline int gre_key_cmpfn(const struct nf_ct_gre_keymap *km, ··· 87 85 struct nf_ct_gre_keymap *km; 88 86 __be16 key = 0; 89 87 90 - read_lock_bh(&net_gre->keymap_lock); 91 - list_for_each_entry(km, &net_gre->keymap_list, list) { 88 + list_for_each_entry_rcu(km, &net_gre->keymap_list, list) { 92 89 if (gre_key_cmpfn(km, t)) { 93 90 key = km->tuple.src.u.gre.key; 94 91 break; 95 92 } 96 93 } 97 - read_unlock_bh(&net_gre->keymap_lock); 98 94 99 95 pr_debug("lookup src key 0x%x for ", key); 100 96 nf_ct_dump_tuple(t); ··· 112 112 kmp = &ct_pptp_info->keymap[dir]; 113 113 if (*kmp) { 114 114 /* check whether it's a retransmission */ 115 - read_lock_bh(&net_gre->keymap_lock); 116 - list_for_each_entry(km, &net_gre->keymap_list, list) { 117 - if (gre_key_cmpfn(km, t) && km == *kmp) { 118 - read_unlock_bh(&net_gre->keymap_lock); 115 + list_for_each_entry_rcu(km, &net_gre->keymap_list, list) { 116 + if (gre_key_cmpfn(km, t) && km == *kmp) 119 117 return 0; 120 - } 121 118 } 122 - read_unlock_bh(&net_gre->keymap_lock); 123 119 pr_debug("trying to override keymap_%s for ct %p\n", 124 120 dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct); 125 121 return -EEXIST; ··· 130 134 pr_debug("adding new entry %p: ", km); 131 135 nf_ct_dump_tuple(&km->tuple); 132 136 133 - write_lock_bh(&net_gre->keymap_lock); 137 + spin_lock_bh(&keymap_lock); 134 138 list_add_tail(&km->list, &net_gre->keymap_list); 135 - write_unlock_bh(&net_gre->keymap_lock); 139 + spin_unlock_bh(&keymap_lock); 136 140 137 141 return 0; 138 142 } ··· 141 145 /* destroy the keymap entries associated with specified master ct */ 142 146 void nf_ct_gre_keymap_destroy(struct nf_conn *ct) 143 147 { 144 - struct net *net = nf_ct_net(ct); 145 - struct netns_proto_gre *net_gre = gre_pernet(net); 146 148 struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct); 147 149 enum ip_conntrack_dir dir; 148 150 149 151 pr_debug("entering for ct %p\n", ct); 150 152 151 - write_lock_bh(&net_gre->keymap_lock); 153 + spin_lock_bh(&keymap_lock); 152 154 for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) { 153 155 if (ct_pptp_info->keymap[dir]) { 154 156 pr_debug("removing %p from list\n", 155 157 ct_pptp_info->keymap[dir]); 156 - list_del(&ct_pptp_info->keymap[dir]->list); 157 - kfree(ct_pptp_info->keymap[dir]); 158 + list_del_rcu(&ct_pptp_info->keymap[dir]->list); 159 + kfree_rcu(ct_pptp_info->keymap[dir], rcu); 158 160 ct_pptp_info->keymap[dir] = NULL; 159 161 } 160 162 } 161 - write_unlock_bh(&net_gre->keymap_lock); 163 + spin_unlock_bh(&keymap_lock); 162 164 } 163 165 EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_destroy); 164 166 ··· 359 365 struct nf_proto_net *nf = &net_gre->nf; 360 366 int i; 361 367 362 - rwlock_init(&net_gre->keymap_lock); 363 368 INIT_LIST_HEAD(&net_gre->keymap_list); 364 369 for (i = 0; i < GRE_CT_MAX; i++) 365 370 net_gre->gre_timeouts[i] = gre_timeouts[i];