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

netfilter: make xt_rateest hash table per net

As suggested by Eric, we need to make the xt_rateest
hash table and its lock per netns to reduce lock
contentions.

Cc: Florian Westphal <fw@strlen.de>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Cong Wang and committed by
Pablo Neira Ayuso
3427b2ab 0d7df906

+72 -33
+2 -2
include/net/netfilter/xt_rateest.h
··· 21 21 struct net_rate_estimator __rcu *rate_est; 22 22 }; 23 23 24 - struct xt_rateest *xt_rateest_lookup(const char *name); 25 - void xt_rateest_put(struct xt_rateest *est); 24 + struct xt_rateest *xt_rateest_lookup(struct net *net, const char *name); 25 + void xt_rateest_put(struct net *net, struct xt_rateest *est); 26 26 27 27 #endif /* _XT_RATEEST_H */
+65 -26
net/netfilter/xt_RATEEST.c
··· 14 14 #include <linux/slab.h> 15 15 #include <net/gen_stats.h> 16 16 #include <net/netlink.h> 17 + #include <net/netns/generic.h> 17 18 18 19 #include <linux/netfilter/x_tables.h> 19 20 #include <linux/netfilter/xt_RATEEST.h> 20 21 #include <net/netfilter/xt_rateest.h> 21 22 22 - static DEFINE_MUTEX(xt_rateest_mutex); 23 - 24 23 #define RATEEST_HSIZE 16 25 - static struct hlist_head rateest_hash[RATEEST_HSIZE] __read_mostly; 24 + 25 + struct xt_rateest_net { 26 + struct mutex hash_lock; 27 + struct hlist_head hash[RATEEST_HSIZE]; 28 + }; 29 + 30 + static unsigned int xt_rateest_id; 31 + 26 32 static unsigned int jhash_rnd __read_mostly; 27 33 28 34 static unsigned int xt_rateest_hash(const char *name) ··· 37 31 (RATEEST_HSIZE - 1); 38 32 } 39 33 40 - static void xt_rateest_hash_insert(struct xt_rateest *est) 34 + static void xt_rateest_hash_insert(struct xt_rateest_net *xn, 35 + struct xt_rateest *est) 41 36 { 42 37 unsigned int h; 43 38 44 39 h = xt_rateest_hash(est->name); 45 - hlist_add_head(&est->list, &rateest_hash[h]); 40 + hlist_add_head(&est->list, &xn->hash[h]); 46 41 } 47 42 48 - static struct xt_rateest *__xt_rateest_lookup(const char *name) 43 + static struct xt_rateest *__xt_rateest_lookup(struct xt_rateest_net *xn, 44 + const char *name) 49 45 { 50 46 struct xt_rateest *est; 51 47 unsigned int h; 52 48 53 49 h = xt_rateest_hash(name); 54 - hlist_for_each_entry(est, &rateest_hash[h], list) { 50 + hlist_for_each_entry(est, &xn->hash[h], list) { 55 51 if (strcmp(est->name, name) == 0) { 56 52 est->refcnt++; 57 53 return est; ··· 63 55 return NULL; 64 56 } 65 57 66 - struct xt_rateest *xt_rateest_lookup(const char *name) 58 + struct xt_rateest *xt_rateest_lookup(struct net *net, const char *name) 67 59 { 60 + struct xt_rateest_net *xn = net_generic(net, xt_rateest_id); 68 61 struct xt_rateest *est; 69 62 70 - mutex_lock(&xt_rateest_mutex); 71 - est = __xt_rateest_lookup(name); 72 - mutex_unlock(&xt_rateest_mutex); 63 + mutex_lock(&xn->hash_lock); 64 + est = __xt_rateest_lookup(xn, name); 65 + mutex_unlock(&xn->hash_lock); 73 66 return est; 74 67 } 75 68 EXPORT_SYMBOL_GPL(xt_rateest_lookup); 76 69 77 - void xt_rateest_put(struct xt_rateest *est) 70 + void xt_rateest_put(struct net *net, struct xt_rateest *est) 78 71 { 79 - mutex_lock(&xt_rateest_mutex); 72 + struct xt_rateest_net *xn = net_generic(net, xt_rateest_id); 73 + 74 + mutex_lock(&xn->hash_lock); 80 75 if (--est->refcnt == 0) { 81 76 hlist_del(&est->list); 82 77 gen_kill_estimator(&est->rate_est); ··· 89 78 */ 90 79 kfree_rcu(est, rcu); 91 80 } 92 - mutex_unlock(&xt_rateest_mutex); 81 + mutex_unlock(&xn->hash_lock); 93 82 } 94 83 EXPORT_SYMBOL_GPL(xt_rateest_put); 95 84 ··· 109 98 110 99 static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par) 111 100 { 101 + struct xt_rateest_net *xn = net_generic(par->net, xt_rateest_id); 112 102 struct xt_rateest_target_info *info = par->targinfo; 113 103 struct xt_rateest *est; 114 104 struct { ··· 120 108 121 109 net_get_random_once(&jhash_rnd, sizeof(jhash_rnd)); 122 110 123 - mutex_lock(&xt_rateest_mutex); 124 - est = __xt_rateest_lookup(info->name); 111 + mutex_lock(&xn->hash_lock); 112 + est = __xt_rateest_lookup(xn, info->name); 125 113 if (est) { 126 - mutex_unlock(&xt_rateest_mutex); 114 + mutex_unlock(&xn->hash_lock); 127 115 /* 128 116 * If estimator parameters are specified, they must match the 129 117 * existing estimator. ··· 131 119 if ((!info->interval && !info->ewma_log) || 132 120 (info->interval != est->params.interval || 133 121 info->ewma_log != est->params.ewma_log)) { 134 - xt_rateest_put(est); 122 + xt_rateest_put(par->net, est); 135 123 return -EINVAL; 136 124 } 137 125 info->est = est; ··· 160 148 goto err2; 161 149 162 150 info->est = est; 163 - xt_rateest_hash_insert(est); 164 - mutex_unlock(&xt_rateest_mutex); 151 + xt_rateest_hash_insert(xn, est); 152 + mutex_unlock(&xn->hash_lock); 165 153 return 0; 166 154 167 155 err2: 168 156 kfree(est); 169 157 err1: 170 - mutex_unlock(&xt_rateest_mutex); 158 + mutex_unlock(&xn->hash_lock); 171 159 return ret; 172 160 } 173 161 ··· 175 163 { 176 164 struct xt_rateest_target_info *info = par->targinfo; 177 165 178 - xt_rateest_put(info->est); 166 + xt_rateest_put(par->net, info->est); 179 167 } 180 168 181 169 static struct xt_target xt_rateest_tg_reg __read_mostly = { ··· 190 178 .me = THIS_MODULE, 191 179 }; 192 180 181 + static __net_init int xt_rateest_net_init(struct net *net) 182 + { 183 + struct xt_rateest_net *xn = net_generic(net, xt_rateest_id); 184 + int i; 185 + 186 + mutex_init(&xn->hash_lock); 187 + for (i = 0; i < ARRAY_SIZE(xn->hash); i++) 188 + INIT_HLIST_HEAD(&xn->hash[i]); 189 + return 0; 190 + } 191 + 192 + static void __net_exit xt_rateest_net_exit(struct net *net) 193 + { 194 + struct xt_rateest_net *xn = net_generic(net, xt_rateest_id); 195 + int i; 196 + 197 + for (i = 0; i < ARRAY_SIZE(xn->hash); i++) 198 + WARN_ON_ONCE(!hlist_empty(&xn->hash[i])); 199 + } 200 + 201 + static struct pernet_operations xt_rateest_net_ops = { 202 + .init = xt_rateest_net_init, 203 + .exit = xt_rateest_net_exit, 204 + .id = &xt_rateest_id, 205 + .size = sizeof(struct xt_rateest_net), 206 + }; 207 + 193 208 static int __init xt_rateest_tg_init(void) 194 209 { 195 - unsigned int i; 210 + int err = register_pernet_subsys(&xt_rateest_net_ops); 196 211 197 - for (i = 0; i < ARRAY_SIZE(rateest_hash); i++) 198 - INIT_HLIST_HEAD(&rateest_hash[i]); 199 - 212 + if (err) 213 + return err; 200 214 return xt_register_target(&xt_rateest_tg_reg); 201 215 } 202 216 203 217 static void __exit xt_rateest_tg_fini(void) 204 218 { 205 219 xt_unregister_target(&xt_rateest_tg_reg); 220 + unregister_pernet_subsys(&xt_rateest_net_ops); 206 221 } 207 222 208 223
+5 -5
net/netfilter/xt_rateest.c
··· 95 95 } 96 96 97 97 ret = -ENOENT; 98 - est1 = xt_rateest_lookup(info->name1); 98 + est1 = xt_rateest_lookup(par->net, info->name1); 99 99 if (!est1) 100 100 goto err1; 101 101 102 102 est2 = NULL; 103 103 if (info->flags & XT_RATEEST_MATCH_REL) { 104 - est2 = xt_rateest_lookup(info->name2); 104 + est2 = xt_rateest_lookup(par->net, info->name2); 105 105 if (!est2) 106 106 goto err2; 107 107 } ··· 111 111 return 0; 112 112 113 113 err2: 114 - xt_rateest_put(est1); 114 + xt_rateest_put(par->net, est1); 115 115 err1: 116 116 return ret; 117 117 } ··· 120 120 { 121 121 struct xt_rateest_match_info *info = par->matchinfo; 122 122 123 - xt_rateest_put(info->est1); 123 + xt_rateest_put(par->net, info->est1); 124 124 if (info->est2) 125 - xt_rateest_put(info->est2); 125 + xt_rateest_put(par->net, info->est2); 126 126 } 127 127 128 128 static struct xt_match xt_rateest_mt_reg __read_mostly = {