mm: add a low limit to alloc_large_system_hash
authorTim Bird <tim.bird@am.sony.com>
Wed, 23 May 2012 13:33:35 +0000 (13:33 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 24 May 2012 04:28:21 +0000 (00:28 -0400)
UDP stack needs a minimum hash size value for proper operation and also
uses alloc_large_system_hash() for proper NUMA distribution of its hash
tables and automatic sizing depending on available system memory.

On some low memory situations, udp_table_init() must ignore the
alloc_large_system_hash() result and reallocs a bigger memory area.

As we cannot easily free old hash table, we leak it and kmemleak can
issue a warning.

This patch adds a low limit parameter to alloc_large_system_hash() to
solve this problem.

We then specify UDP_HTABLE_SIZE_MIN for UDP/UDPLite hash table
allocation.

Reported-by: Mark Asselstine <mark.asselstine@windriver.com>
Reported-by: Tim Bird <tim.bird@am.sony.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Paul Gortmaker <paul.gortmaker@windriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
fs/dcache.c
fs/inode.c
include/linux/bootmem.h
kernel/pid.c
mm/page_alloc.c
net/ipv4/route.c
net/ipv4/tcp.c
net/ipv4/udp.c

index 8c1ab8fb501228e082c04160fbc69aaeafd3cbee..4435d8b329044da3b48c83dfe555409464797d0d 100644 (file)
@@ -3093,6 +3093,7 @@ static void __init dcache_init_early(void)
                                        HASH_EARLY,
                                        &d_hash_shift,
                                        &d_hash_mask,
+                                       0,
                                        0);
 
        for (loop = 0; loop < (1U << d_hash_shift); loop++)
@@ -3123,6 +3124,7 @@ static void __init dcache_init(void)
                                        0,
                                        &d_hash_shift,
                                        &d_hash_mask,
+                                       0,
                                        0);
 
        for (loop = 0; loop < (1U << d_hash_shift); loop++)
index 9f4f5fecc0963c12db1f806be61999a26a9ba7ea..e3ef2573cbdfb2e3637061bc1ffe31c2a65d7864 100644 (file)
@@ -1647,6 +1647,7 @@ void __init inode_init_early(void)
                                        HASH_EARLY,
                                        &i_hash_shift,
                                        &i_hash_mask,
+                                       0,
                                        0);
 
        for (loop = 0; loop < (1U << i_hash_shift); loop++)
@@ -1677,6 +1678,7 @@ void __init inode_init(void)
                                        0,
                                        &i_hash_shift,
                                        &i_hash_mask,
+                                       0,
                                        0);
 
        for (loop = 0; loop < (1U << i_hash_shift); loop++)
index 66d3e954eb6c023de382ab71c7425de622a62663..1a0cd270bb7a0850f643cc3902aa5f943279ec8c 100644 (file)
@@ -154,7 +154,8 @@ extern void *alloc_large_system_hash(const char *tablename,
                                     int flags,
                                     unsigned int *_hash_shift,
                                     unsigned int *_hash_mask,
-                                    unsigned long limit);
+                                    unsigned long low_limit,
+                                    unsigned long high_limit);
 
 #define HASH_EARLY     0x00000001      /* Allocating during early boot? */
 #define HASH_SMALL     0x00000002      /* sub-page allocation allowed, min
index 9f08dfabaf13af1f79a8ae4c5bb87a4434736d18..e86b291ad83467d9b828691ab146b962931104d6 100644 (file)
@@ -547,7 +547,8 @@ void __init pidhash_init(void)
 
        pid_hash = alloc_large_system_hash("PID", sizeof(*pid_hash), 0, 18,
                                           HASH_EARLY | HASH_SMALL,
-                                          &pidhash_shift, NULL, 4096);
+                                          &pidhash_shift, NULL,
+                                          0, 4096);
        pidhash_size = 1U << pidhash_shift;
 
        for (i = 0; i < pidhash_size; i++)
index 918330f71dbae794a99139c1d708da0f16961a29..b7af568f0ed91e8a2026abccc1fbb4ad6105a8ce 100644 (file)
@@ -5242,9 +5242,10 @@ void *__init alloc_large_system_hash(const char *tablename,
                                     int flags,
                                     unsigned int *_hash_shift,
                                     unsigned int *_hash_mask,
-                                    unsigned long limit)
+                                    unsigned long low_limit,
+                                    unsigned long high_limit)
 {
-       unsigned long long max = limit;
+       unsigned long long max = high_limit;
        unsigned long log2qty, size;
        void *table = NULL;
 
@@ -5282,6 +5283,8 @@ void *__init alloc_large_system_hash(const char *tablename,
        }
        max = min(max, 0x80000000ULL);
 
+       if (numentries < low_limit)
+               numentries = low_limit;
        if (numentries > max)
                numentries = max;
 
index ffcb3b016843ff79976cee50ce6e9f332591c0ac..98b30d08efe99b1673ece1d590a70ecf88967a1a 100644 (file)
@@ -3452,6 +3452,7 @@ int __init ip_rt_init(void)
                                        0,
                                        &rt_hash_log,
                                        &rt_hash_mask,
+                                       0,
                                        rhash_entries ? 0 : 512 * 1024);
        memset(rt_hash_table, 0, (rt_hash_mask + 1) * sizeof(struct rt_hash_bucket));
        rt_hash_lock_init();
index bb485fcb077ef0cf290ce04423b24021190cf4c0..3ba605f60e4e18b0b83cfc882cf8fd9ed39569f3 100644 (file)
@@ -3514,6 +3514,7 @@ void __init tcp_init(void)
                                        0,
                                        NULL,
                                        &tcp_hashinfo.ehash_mask,
+                                       0,
                                        thash_entries ? 0 : 512 * 1024);
        for (i = 0; i <= tcp_hashinfo.ehash_mask; i++) {
                INIT_HLIST_NULLS_HEAD(&tcp_hashinfo.ehash[i].chain, i);
@@ -3530,6 +3531,7 @@ void __init tcp_init(void)
                                        0,
                                        &tcp_hashinfo.bhash_size,
                                        NULL,
+                                       0,
                                        64 * 1024);
        tcp_hashinfo.bhash_size = 1U << tcp_hashinfo.bhash_size;
        for (i = 0; i < tcp_hashinfo.bhash_size; i++) {
index 609397ee78fbd3ec9d16c249a6f023dd6903594f..eaca73644e7965e5405b6e61f362a3be3e0d2e2f 100644 (file)
@@ -2192,26 +2192,16 @@ void __init udp_table_init(struct udp_table *table, const char *name)
 {
        unsigned int i;
 
-       if (!CONFIG_BASE_SMALL)
-               table->hash = alloc_large_system_hash(name,
-                       2 * sizeof(struct udp_hslot),
-                       uhash_entries,
-                       21, /* one slot per 2 MB */
-                       0,
-                       &table->log,
-                       &table->mask,
-                       64 * 1024);
-       /*
-        * Make sure hash table has the minimum size
-        */
-       if (CONFIG_BASE_SMALL || table->mask < UDP_HTABLE_SIZE_MIN - 1) {
-               table->hash = kmalloc(UDP_HTABLE_SIZE_MIN *
-                                     2 * sizeof(struct udp_hslot), GFP_KERNEL);
-               if (!table->hash)
-                       panic(name);
-               table->log = ilog2(UDP_HTABLE_SIZE_MIN);
-               table->mask = UDP_HTABLE_SIZE_MIN - 1;
-       }
+       table->hash = alloc_large_system_hash(name,
+                                             2 * sizeof(struct udp_hslot),
+                                             uhash_entries,
+                                             21, /* one slot per 2 MB */
+                                             0,
+                                             &table->log,
+                                             &table->mask,
+                                             UDP_HTABLE_SIZE_MIN,
+                                             64 * 1024);
+
        table->hash2 = table->hash + (table->mask + 1);
        for (i = 0; i <= table->mask; i++) {
                INIT_HLIST_NULLS_HEAD(&table->hash[i].head, i);