aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/netfilter/xt_hashlimit.c52
1 files changed, 18 insertions, 34 deletions
diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
index fb7fcb773a3f..017c95966aa8 100644
--- a/net/netfilter/xt_hashlimit.c
+++ b/net/netfilter/xt_hashlimit.c
@@ -90,7 +90,7 @@ struct dsthash_ent {
90 90
91struct xt_hashlimit_htable { 91struct xt_hashlimit_htable {
92 struct hlist_node node; /* global list of all htables */ 92 struct hlist_node node; /* global list of all htables */
93 atomic_t use; 93 int use;
94 u_int8_t family; 94 u_int8_t family;
95 bool rnd_initialized; 95 bool rnd_initialized;
96 96
@@ -109,8 +109,7 @@ struct xt_hashlimit_htable {
109 struct hlist_head hash[0]; /* hashtable itself */ 109 struct hlist_head hash[0]; /* hashtable itself */
110}; 110};
111 111
112static DEFINE_SPINLOCK(hashlimit_lock); /* protects htables list */ 112static DEFINE_MUTEX(hashlimit_mutex); /* protects htables list */
113static DEFINE_MUTEX(hlimit_mutex); /* additional checkentry protection */
114static struct kmem_cache *hashlimit_cachep __read_mostly; 113static struct kmem_cache *hashlimit_cachep __read_mostly;
115 114
116static inline bool dst_cmp(const struct dsthash_ent *ent, 115static inline bool dst_cmp(const struct dsthash_ent *ent,
@@ -244,7 +243,7 @@ static int htable_create_v0(struct net *net, struct xt_hashlimit_info *minfo, u_
244 for (i = 0; i < hinfo->cfg.size; i++) 243 for (i = 0; i < hinfo->cfg.size; i++)
245 INIT_HLIST_HEAD(&hinfo->hash[i]); 244 INIT_HLIST_HEAD(&hinfo->hash[i]);
246 245
247 atomic_set(&hinfo->use, 1); 246 hinfo->use = 1;
248 hinfo->count = 0; 247 hinfo->count = 0;
249 hinfo->family = family; 248 hinfo->family = family;
250 hinfo->rnd_initialized = false; 249 hinfo->rnd_initialized = false;
@@ -263,9 +262,9 @@ static int htable_create_v0(struct net *net, struct xt_hashlimit_info *minfo, u_
263 hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); 262 hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval);
264 add_timer(&hinfo->timer); 263 add_timer(&hinfo->timer);
265 264
266 spin_lock_bh(&hashlimit_lock); 265 mutex_lock(&hashlimit_mutex);
267 hlist_add_head(&hinfo->node, &hashlimit_net->htables); 266 hlist_add_head(&hinfo->node, &hashlimit_net->htables);
268 spin_unlock_bh(&hashlimit_lock); 267 mutex_unlock(&hashlimit_mutex);
269 268
270 return 0; 269 return 0;
271} 270}
@@ -308,7 +307,7 @@ static int htable_create(struct net *net, struct xt_hashlimit_mtinfo1 *minfo,
308 for (i = 0; i < hinfo->cfg.size; i++) 307 for (i = 0; i < hinfo->cfg.size; i++)
309 INIT_HLIST_HEAD(&hinfo->hash[i]); 308 INIT_HLIST_HEAD(&hinfo->hash[i]);
310 309
311 atomic_set(&hinfo->use, 1); 310 hinfo->use = 1;
312 hinfo->count = 0; 311 hinfo->count = 0;
313 hinfo->family = family; 312 hinfo->family = family;
314 hinfo->rnd_initialized = false; 313 hinfo->rnd_initialized = false;
@@ -328,9 +327,9 @@ static int htable_create(struct net *net, struct xt_hashlimit_mtinfo1 *minfo,
328 hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); 327 hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval);
329 add_timer(&hinfo->timer); 328 add_timer(&hinfo->timer);
330 329
331 spin_lock_bh(&hashlimit_lock); 330 mutex_lock(&hashlimit_mutex);
332 hlist_add_head(&hinfo->node, &hashlimit_net->htables); 331 hlist_add_head(&hinfo->node, &hashlimit_net->htables);
333 spin_unlock_bh(&hashlimit_lock); 332 mutex_unlock(&hashlimit_mutex);
334 333
335 return 0; 334 return 0;
336} 335}
@@ -402,27 +401,24 @@ static struct xt_hashlimit_htable *htable_find_get(struct net *net,
402 struct xt_hashlimit_htable *hinfo; 401 struct xt_hashlimit_htable *hinfo;
403 struct hlist_node *pos; 402 struct hlist_node *pos;
404 403
405 spin_lock_bh(&hashlimit_lock);
406 hlist_for_each_entry(hinfo, pos, &hashlimit_net->htables, node) { 404 hlist_for_each_entry(hinfo, pos, &hashlimit_net->htables, node) {
407 if (!strcmp(name, hinfo->pde->name) && 405 if (!strcmp(name, hinfo->pde->name) &&
408 hinfo->family == family) { 406 hinfo->family == family) {
409 atomic_inc(&hinfo->use); 407 hinfo->use++;
410 spin_unlock_bh(&hashlimit_lock);
411 return hinfo; 408 return hinfo;
412 } 409 }
413 } 410 }
414 spin_unlock_bh(&hashlimit_lock);
415 return NULL; 411 return NULL;
416} 412}
417 413
418static void htable_put(struct xt_hashlimit_htable *hinfo) 414static void htable_put(struct xt_hashlimit_htable *hinfo)
419{ 415{
420 if (atomic_dec_and_test(&hinfo->use)) { 416 mutex_lock(&hashlimit_mutex);
421 spin_lock_bh(&hashlimit_lock); 417 if (--hinfo->use == 0) {
422 hlist_del(&hinfo->node); 418 hlist_del(&hinfo->node);
423 spin_unlock_bh(&hashlimit_lock);
424 htable_destroy(hinfo); 419 htable_destroy(hinfo);
425 } 420 }
421 mutex_unlock(&hashlimit_mutex);
426} 422}
427 423
428/* The algorithm used is the Simple Token Bucket Filter (TBF) 424/* The algorithm used is the Simple Token Bucket Filter (TBF)
@@ -710,19 +706,13 @@ static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par)
710 if (r->name[sizeof(r->name) - 1] != '\0') 706 if (r->name[sizeof(r->name) - 1] != '\0')
711 return false; 707 return false;
712 708
713 /* This is the best we've got: We cannot release and re-grab lock, 709 mutex_lock(&hashlimit_mutex);
714 * since checkentry() is called before x_tables.c grabs xt_mutex.
715 * We also cannot grab the hashtable spinlock, since htable_create will
716 * call vmalloc, and that can sleep. And we cannot just re-search
717 * the list of htable's in htable_create(), since then we would
718 * create duplicate proc files. -HW */
719 mutex_lock(&hlimit_mutex);
720 r->hinfo = htable_find_get(net, r->name, par->match->family); 710 r->hinfo = htable_find_get(net, r->name, par->match->family);
721 if (!r->hinfo && htable_create_v0(net, r, par->match->family) != 0) { 711 if (!r->hinfo && htable_create_v0(net, r, par->match->family) != 0) {
722 mutex_unlock(&hlimit_mutex); 712 mutex_unlock(&hashlimit_mutex);
723 return false; 713 return false;
724 } 714 }
725 mutex_unlock(&hlimit_mutex); 715 mutex_unlock(&hashlimit_mutex);
726 716
727 return true; 717 return true;
728} 718}
@@ -752,19 +742,13 @@ static bool hashlimit_mt_check(const struct xt_mtchk_param *par)
752 return false; 742 return false;
753 } 743 }
754 744
755 /* This is the best we've got: We cannot release and re-grab lock, 745 mutex_lock(&hashlimit_mutex);
756 * since checkentry() is called before x_tables.c grabs xt_mutex.
757 * We also cannot grab the hashtable spinlock, since htable_create will
758 * call vmalloc, and that can sleep. And we cannot just re-search
759 * the list of htable's in htable_create(), since then we would
760 * create duplicate proc files. -HW */
761 mutex_lock(&hlimit_mutex);
762 info->hinfo = htable_find_get(net, info->name, par->match->family); 746 info->hinfo = htable_find_get(net, info->name, par->match->family);
763 if (!info->hinfo && htable_create(net, info, par->match->family) != 0) { 747 if (!info->hinfo && htable_create(net, info, par->match->family) != 0) {
764 mutex_unlock(&hlimit_mutex); 748 mutex_unlock(&hashlimit_mutex);
765 return false; 749 return false;
766 } 750 }
767 mutex_unlock(&hlimit_mutex); 751 mutex_unlock(&hashlimit_mutex);
768 return true; 752 return true;
769} 753}
770 754