aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Bird <tim.bird@am.sony.com>2012-05-23 09:33:35 -0400
committerDavid S. Miller <davem@davemloft.net>2012-05-24 00:28:21 -0400
commit31fe62b9586643953f0c0c37a6357dafc69034e2 (patch)
tree69f9990423969df4ecbaea9d1e8de748284bea5e
parentd0a24a3516fb36023bef28d2355fa34e7f32029f (diff)
mm: add a low limit to alloc_large_system_hash
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>
-rw-r--r--fs/dcache.c2
-rw-r--r--fs/inode.c2
-rw-r--r--include/linux/bootmem.h3
-rw-r--r--kernel/pid.c3
-rw-r--r--mm/page_alloc.c7
-rw-r--r--net/ipv4/route.c1
-rw-r--r--net/ipv4/tcp.c2
-rw-r--r--net/ipv4/udp.c30
8 files changed, 26 insertions, 24 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 8c1ab8fb5012..4435d8b32904 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -3093,6 +3093,7 @@ static void __init dcache_init_early(void)
3093 HASH_EARLY, 3093 HASH_EARLY,
3094 &d_hash_shift, 3094 &d_hash_shift,
3095 &d_hash_mask, 3095 &d_hash_mask,
3096 0,
3096 0); 3097 0);
3097 3098
3098 for (loop = 0; loop < (1U << d_hash_shift); loop++) 3099 for (loop = 0; loop < (1U << d_hash_shift); loop++)
@@ -3123,6 +3124,7 @@ static void __init dcache_init(void)
3123 0, 3124 0,
3124 &d_hash_shift, 3125 &d_hash_shift,
3125 &d_hash_mask, 3126 &d_hash_mask,
3127 0,
3126 0); 3128 0);
3127 3129
3128 for (loop = 0; loop < (1U << d_hash_shift); loop++) 3130 for (loop = 0; loop < (1U << d_hash_shift); loop++)
diff --git a/fs/inode.c b/fs/inode.c
index 9f4f5fecc096..e3ef2573cbdf 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1647,6 +1647,7 @@ void __init inode_init_early(void)
1647 HASH_EARLY, 1647 HASH_EARLY,
1648 &i_hash_shift, 1648 &i_hash_shift,
1649 &i_hash_mask, 1649 &i_hash_mask,
1650 0,
1650 0); 1651 0);
1651 1652
1652 for (loop = 0; loop < (1U << i_hash_shift); loop++) 1653 for (loop = 0; loop < (1U << i_hash_shift); loop++)
@@ -1677,6 +1678,7 @@ void __init inode_init(void)
1677 0, 1678 0,
1678 &i_hash_shift, 1679 &i_hash_shift,
1679 &i_hash_mask, 1680 &i_hash_mask,
1681 0,
1680 0); 1682 0);
1681 1683
1682 for (loop = 0; loop < (1U << i_hash_shift); loop++) 1684 for (loop = 0; loop < (1U << i_hash_shift); loop++)
diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h
index 66d3e954eb6c..1a0cd270bb7a 100644
--- a/include/linux/bootmem.h
+++ b/include/linux/bootmem.h
@@ -154,7 +154,8 @@ extern void *alloc_large_system_hash(const char *tablename,
154 int flags, 154 int flags,
155 unsigned int *_hash_shift, 155 unsigned int *_hash_shift,
156 unsigned int *_hash_mask, 156 unsigned int *_hash_mask,
157 unsigned long limit); 157 unsigned long low_limit,
158 unsigned long high_limit);
158 159
159#define HASH_EARLY 0x00000001 /* Allocating during early boot? */ 160#define HASH_EARLY 0x00000001 /* Allocating during early boot? */
160#define HASH_SMALL 0x00000002 /* sub-page allocation allowed, min 161#define HASH_SMALL 0x00000002 /* sub-page allocation allowed, min
diff --git a/kernel/pid.c b/kernel/pid.c
index 9f08dfabaf13..e86b291ad834 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -547,7 +547,8 @@ void __init pidhash_init(void)
547 547
548 pid_hash = alloc_large_system_hash("PID", sizeof(*pid_hash), 0, 18, 548 pid_hash = alloc_large_system_hash("PID", sizeof(*pid_hash), 0, 18,
549 HASH_EARLY | HASH_SMALL, 549 HASH_EARLY | HASH_SMALL,
550 &pidhash_shift, NULL, 4096); 550 &pidhash_shift, NULL,
551 0, 4096);
551 pidhash_size = 1U << pidhash_shift; 552 pidhash_size = 1U << pidhash_shift;
552 553
553 for (i = 0; i < pidhash_size; i++) 554 for (i = 0; i < pidhash_size; i++)
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 918330f71dba..b7af568f0ed9 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -5242,9 +5242,10 @@ void *__init alloc_large_system_hash(const char *tablename,
5242 int flags, 5242 int flags,
5243 unsigned int *_hash_shift, 5243 unsigned int *_hash_shift,
5244 unsigned int *_hash_mask, 5244 unsigned int *_hash_mask,
5245 unsigned long limit) 5245 unsigned long low_limit,
5246 unsigned long high_limit)
5246{ 5247{
5247 unsigned long long max = limit; 5248 unsigned long long max = high_limit;
5248 unsigned long log2qty, size; 5249 unsigned long log2qty, size;
5249 void *table = NULL; 5250 void *table = NULL;
5250 5251
@@ -5282,6 +5283,8 @@ void *__init alloc_large_system_hash(const char *tablename,
5282 } 5283 }
5283 max = min(max, 0x80000000ULL); 5284 max = min(max, 0x80000000ULL);
5284 5285
5286 if (numentries < low_limit)
5287 numentries = low_limit;
5285 if (numentries > max) 5288 if (numentries > max)
5286 numentries = max; 5289 numentries = max;
5287 5290
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index ffcb3b016843..98b30d08efe9 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -3452,6 +3452,7 @@ int __init ip_rt_init(void)
3452 0, 3452 0,
3453 &rt_hash_log, 3453 &rt_hash_log,
3454 &rt_hash_mask, 3454 &rt_hash_mask,
3455 0,
3455 rhash_entries ? 0 : 512 * 1024); 3456 rhash_entries ? 0 : 512 * 1024);
3456 memset(rt_hash_table, 0, (rt_hash_mask + 1) * sizeof(struct rt_hash_bucket)); 3457 memset(rt_hash_table, 0, (rt_hash_mask + 1) * sizeof(struct rt_hash_bucket));
3457 rt_hash_lock_init(); 3458 rt_hash_lock_init();
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index bb485fcb077e..3ba605f60e4e 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -3514,6 +3514,7 @@ void __init tcp_init(void)
3514 0, 3514 0,
3515 NULL, 3515 NULL,
3516 &tcp_hashinfo.ehash_mask, 3516 &tcp_hashinfo.ehash_mask,
3517 0,
3517 thash_entries ? 0 : 512 * 1024); 3518 thash_entries ? 0 : 512 * 1024);
3518 for (i = 0; i <= tcp_hashinfo.ehash_mask; i++) { 3519 for (i = 0; i <= tcp_hashinfo.ehash_mask; i++) {
3519 INIT_HLIST_NULLS_HEAD(&tcp_hashinfo.ehash[i].chain, i); 3520 INIT_HLIST_NULLS_HEAD(&tcp_hashinfo.ehash[i].chain, i);
@@ -3530,6 +3531,7 @@ void __init tcp_init(void)
3530 0, 3531 0,
3531 &tcp_hashinfo.bhash_size, 3532 &tcp_hashinfo.bhash_size,
3532 NULL, 3533 NULL,
3534 0,
3533 64 * 1024); 3535 64 * 1024);
3534 tcp_hashinfo.bhash_size = 1U << tcp_hashinfo.bhash_size; 3536 tcp_hashinfo.bhash_size = 1U << tcp_hashinfo.bhash_size;
3535 for (i = 0; i < tcp_hashinfo.bhash_size; i++) { 3537 for (i = 0; i < tcp_hashinfo.bhash_size; i++) {
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 609397ee78fb..eaca73644e79 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -2192,26 +2192,16 @@ void __init udp_table_init(struct udp_table *table, const char *name)
2192{ 2192{
2193 unsigned int i; 2193 unsigned int i;
2194 2194
2195 if (!CONFIG_BASE_SMALL) 2195 table->hash = alloc_large_system_hash(name,
2196 table->hash = alloc_large_system_hash(name, 2196 2 * sizeof(struct udp_hslot),
2197 2 * sizeof(struct udp_hslot), 2197 uhash_entries,
2198 uhash_entries, 2198 21, /* one slot per 2 MB */
2199 21, /* one slot per 2 MB */ 2199 0,
2200 0, 2200 &table->log,
2201 &table->log, 2201 &table->mask,
2202 &table->mask, 2202 UDP_HTABLE_SIZE_MIN,
2203 64 * 1024); 2203 64 * 1024);
2204 /* 2204
2205 * Make sure hash table has the minimum size
2206 */
2207 if (CONFIG_BASE_SMALL || table->mask < UDP_HTABLE_SIZE_MIN - 1) {
2208 table->hash = kmalloc(UDP_HTABLE_SIZE_MIN *
2209 2 * sizeof(struct udp_hslot), GFP_KERNEL);
2210 if (!table->hash)
2211 panic(name);
2212 table->log = ilog2(UDP_HTABLE_SIZE_MIN);
2213 table->mask = UDP_HTABLE_SIZE_MIN - 1;
2214 }
2215 table->hash2 = table->hash + (table->mask + 1); 2205 table->hash2 = table->hash + (table->mask + 1);
2216 for (i = 0; i <= table->mask; i++) { 2206 for (i = 0; i <= table->mask; i++) {
2217 INIT_HLIST_NULLS_HEAD(&table->hash[i].head, i); 2207 INIT_HLIST_NULLS_HEAD(&table->hash[i].head, i);