netfilter: use kvmalloc_array to allocate memory for hashtable
authorLi RongQing <lirongqing@baidu.com>
Wed, 25 Jul 2018 07:52:13 +0000 (15:52 +0800)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 3 Aug 2018 16:37:55 +0000 (18:37 +0200)
nf_ct_alloc_hashtable is used to allocate memory for conntrack,
NAT bysrc and expectation hashtable. Assuming 64k bucket size,
which means 7th order page allocation, __get_free_pages, called
by nf_ct_alloc_hashtable, will trigger the direct memory reclaim
and stall for a long time, when system has lots of memory stress

so replace combination of __get_free_pages and vzalloc with
kvmalloc_array, which provides a overflow check and a fallback
if no high order memory is available, and do not retry to reclaim
memory, reduce stall

and remove nf_ct_free_hashtable, since it is just a kvfree

Signed-off-by: Zhang Yu <zhangyu31@baidu.com>
Signed-off-by: Wang Li <wangli39@baidu.com>
Signed-off-by: Li RongQing <lirongqing@baidu.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nf_conntrack.h
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_expect.c
net/netfilter/nf_conntrack_helper.c
net/netfilter/nf_nat_core.c

index a2b0ed0..7e01231 100644 (file)
@@ -176,8 +176,6 @@ void nf_ct_netns_put(struct net *net, u8 nfproto);
  */
 void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls);
 
-void nf_ct_free_hashtable(void *hash, unsigned int size);
-
 int nf_conntrack_hash_check_insert(struct nf_conn *ct);
 bool nf_ct_delete(struct nf_conn *ct, u32 pid, int report);
 
index 8a113ca..a676d5f 100644 (file)
@@ -2022,16 +2022,6 @@ static int kill_all(struct nf_conn *i, void *data)
        return net_eq(nf_ct_net(i), data);
 }
 
-void nf_ct_free_hashtable(void *hash, unsigned int size)
-{
-       if (is_vmalloc_addr(hash))
-               vfree(hash);
-       else
-               free_pages((unsigned long)hash,
-                          get_order(sizeof(struct hlist_head) * size));
-}
-EXPORT_SYMBOL_GPL(nf_ct_free_hashtable);
-
 void nf_conntrack_cleanup_start(void)
 {
        conntrack_gc_work.exiting = true;
@@ -2042,7 +2032,7 @@ void nf_conntrack_cleanup_end(void)
 {
        RCU_INIT_POINTER(nf_ct_hook, NULL);
        cancel_delayed_work_sync(&conntrack_gc_work.dwork);
-       nf_ct_free_hashtable(nf_conntrack_hash, nf_conntrack_htable_size);
+       kvfree(nf_conntrack_hash);
 
        nf_conntrack_proto_fini();
        nf_conntrack_seqadj_fini();
@@ -2108,7 +2098,6 @@ void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls)
 {
        struct hlist_nulls_head *hash;
        unsigned int nr_slots, i;
-       size_t sz;
 
        if (*sizep > (UINT_MAX / sizeof(struct hlist_nulls_head)))
                return NULL;
@@ -2116,14 +2105,8 @@ void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls)
        BUILD_BUG_ON(sizeof(struct hlist_nulls_head) != sizeof(struct hlist_head));
        nr_slots = *sizep = roundup(*sizep, PAGE_SIZE / sizeof(struct hlist_nulls_head));
 
-       if (nr_slots > (UINT_MAX / sizeof(struct hlist_nulls_head)))
-               return NULL;
-
-       sz = nr_slots * sizeof(struct hlist_nulls_head);
-       hash = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO,
-                                       get_order(sz));
-       if (!hash)
-               hash = vzalloc(sz);
+       hash = kvmalloc_array(nr_slots, sizeof(struct hlist_nulls_head),
+                             GFP_KERNEL | __GFP_ZERO);
 
        if (hash && nulls)
                for (i = 0; i < nr_slots; i++)
@@ -2150,7 +2133,7 @@ int nf_conntrack_hash_resize(unsigned int hashsize)
 
        old_size = nf_conntrack_htable_size;
        if (old_size == hashsize) {
-               nf_ct_free_hashtable(hash, hashsize);
+               kvfree(hash);
                return 0;
        }
 
@@ -2186,7 +2169,7 @@ int nf_conntrack_hash_resize(unsigned int hashsize)
        local_bh_enable();
 
        synchronize_net();
-       nf_ct_free_hashtable(old_hash, old_size);
+       kvfree(old_hash);
        return 0;
 }
 
@@ -2350,7 +2333,7 @@ err_acct:
 err_expect:
        kmem_cache_destroy(nf_conntrack_cachep);
 err_cachep:
-       nf_ct_free_hashtable(nf_conntrack_hash, nf_conntrack_htable_size);
+       kvfree(nf_conntrack_hash);
        return ret;
 }
 
index 3f586ba..27b8423 100644 (file)
@@ -712,5 +712,5 @@ void nf_conntrack_expect_fini(void)
 {
        rcu_barrier(); /* Wait for call_rcu() before destroy */
        kmem_cache_destroy(nf_ct_expect_cachep);
-       nf_ct_free_hashtable(nf_ct_expect_hash, nf_ct_expect_hsize);
+       kvfree(nf_ct_expect_hash);
 }
index d557a42..e24b762 100644 (file)
@@ -562,12 +562,12 @@ int nf_conntrack_helper_init(void)
 
        return 0;
 out_extend:
-       nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
+       kvfree(nf_ct_helper_hash);
        return ret;
 }
 
 void nf_conntrack_helper_fini(void)
 {
        nf_ct_extend_unregister(&helper_extend);
-       nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
+       kvfree(nf_ct_helper_hash);
 }
index 6366f0c..e2b1960 100644 (file)
@@ -1056,7 +1056,7 @@ static int __init nf_nat_init(void)
 
        ret = nf_ct_extend_register(&nat_extend);
        if (ret < 0) {
-               nf_ct_free_hashtable(nf_nat_bysource, nf_nat_htable_size);
+               kvfree(nf_nat_bysource);
                pr_err("Unable to register extension\n");
                return ret;
        }
@@ -1094,7 +1094,7 @@ static void __exit nf_nat_cleanup(void)
        for (i = 0; i < NFPROTO_NUMPROTO; i++)
                kfree(nf_nat_l4protos[i]);
        synchronize_net();
-       nf_ct_free_hashtable(nf_nat_bysource, nf_nat_htable_size);
+       kvfree(nf_nat_bysource);
        unregister_pernet_subsys(&nat_net_ops);
 }