diff options
author | Tim Bird <tim.bird@am.sony.com> | 2012-05-23 09:33:35 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-05-24 00:28:21 -0400 |
commit | 31fe62b9586643953f0c0c37a6357dafc69034e2 (patch) | |
tree | 69f9990423969df4ecbaea9d1e8de748284bea5e | |
parent | d0a24a3516fb36023bef28d2355fa34e7f32029f (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.c | 2 | ||||
-rw-r--r-- | fs/inode.c | 2 | ||||
-rw-r--r-- | include/linux/bootmem.h | 3 | ||||
-rw-r--r-- | kernel/pid.c | 3 | ||||
-rw-r--r-- | mm/page_alloc.c | 7 | ||||
-rw-r--r-- | net/ipv4/route.c | 1 | ||||
-rw-r--r-- | net/ipv4/tcp.c | 2 | ||||
-rw-r--r-- | net/ipv4/udp.c | 30 |
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); |