From 31fe62b9586643953f0c0c37a6357dafc69034e2 Mon Sep 17 00:00:00 2001 From: Tim Bird Date: Wed, 23 May 2012 13:33:35 +0000 Subject: 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 Reported-by: Tim Bird Signed-off-by: Eric Dumazet Cc: Paul Gortmaker Signed-off-by: David S. Miller --- net/ipv4/route.c | 1 + net/ipv4/tcp.c | 2 ++ net/ipv4/udp.c | 30 ++++++++++-------------------- 3 files changed, 13 insertions(+), 20 deletions(-) (limited to 'net/ipv4') 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) 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(); 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) 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++) { 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) { 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); -- cgit v1.2.3-59-g8ed1b From e49cc0da7283088c5e03d475ffe2fdcb24a6d5b1 Mon Sep 17 00:00:00 2001 From: Yanmin Zhang Date: Wed, 23 May 2012 15:39:45 +0000 Subject: ipv4: fix the rcu race between free_fib_info and ip_route_output_slow We hit a kernel OOPS. <3>[23898.789643] BUG: sleeping function called from invalid context at /data/buildbot/workdir/ics/hardware/intel/linux-2.6/arch/x86/mm/fault.c:1103 <3>[23898.862215] in_atomic(): 0, irqs_disabled(): 0, pid: 10526, name: Thread-6683 <4>[23898.967805] HSU serial 0000:00:05.1: 0000:00:05.2:HSU serial prevented me to suspend... <4>[23899.258526] Pid: 10526, comm: Thread-6683 Tainted: G W 3.0.8-137685-ge7742f9 #1 <4>[23899.357404] HSU serial 0000:00:05.1: 0000:00:05.2:HSU serial prevented me to suspend... <4>[23899.904225] Call Trace: <4>[23899.989209] [] ? pgtable_bad+0x130/0x130 <4>[23900.000416] [] __might_sleep+0x10a/0x110 <4>[23900.007357] [] do_page_fault+0xd1/0x3c0 <4>[23900.013764] [] ? restore_all+0xf/0xf <4>[23900.024024] [] ? napi_complete+0x8b/0x690 <4>[23900.029297] [] ? pgtable_bad+0x130/0x130 <4>[23900.123739] [] ? pgtable_bad+0x130/0x130 <4>[23900.128955] [] error_code+0x5f/0x64 <4>[23900.133466] [] ? pgtable_bad+0x130/0x130 <4>[23900.138450] [] ? __ip_route_output_key+0x698/0x7c0 <4>[23900.144312] [] ? __ip_route_output_key+0x38d/0x7c0 <4>[23900.150730] [] ip_route_output_flow+0x1f/0x60 <4>[23900.156261] [] ip4_datagram_connect+0x188/0x2b0 <4>[23900.161960] [] ? _raw_spin_unlock_bh+0x1f/0x30 <4>[23900.167834] [] inet_dgram_connect+0x36/0x80 <4>[23900.173224] [] ? _copy_from_user+0x48/0x140 <4>[23900.178817] [] sys_connect+0x9a/0xd0 <4>[23900.183538] [] ? alloc_file+0xdc/0x240 <4>[23900.189111] [] ? sub_preempt_count+0x3d/0x50 Function free_fib_info resets nexthop_nh->nh_dev to NULL before releasing fi. Other cpu might be accessing fi. Fixing it by delaying the releasing. With the patch, we ran MTBF testing on Android mobile for 12 hours and didn't trigger the issue. Thank Eric for very detailed review/checking the issue. Signed-off-by: Yanmin Zhang Signed-off-by: Kun Jiang Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/fib_semantics.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'net/ipv4') diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index a8bdf7405433..e5b7182fa099 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -145,6 +145,12 @@ static void free_fib_info_rcu(struct rcu_head *head) { struct fib_info *fi = container_of(head, struct fib_info, rcu); + change_nexthops(fi) { + if (nexthop_nh->nh_dev) + dev_put(nexthop_nh->nh_dev); + } endfor_nexthops(fi); + + release_net(fi->fib_net); if (fi->fib_metrics != (u32 *) dst_default_metrics) kfree(fi->fib_metrics); kfree(fi); @@ -156,13 +162,7 @@ void free_fib_info(struct fib_info *fi) pr_warn("Freeing alive fib_info %p\n", fi); return; } - change_nexthops(fi) { - if (nexthop_nh->nh_dev) - dev_put(nexthop_nh->nh_dev); - nexthop_nh->nh_dev = NULL; - } endfor_nexthops(fi); fib_info_cnt--; - release_net(fi->fib_net); call_rcu(&fi->rcu, free_fib_info_rcu); } -- cgit v1.2.3-59-g8ed1b From 1ca7ee30630e1022dbcf1b51be20580815ffab73 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 23 May 2012 17:51:37 +0000 Subject: tcp: take care of overlaps in tcp_try_coalesce() Sergio Correia reported following warning : WARNING: at net/ipv4/tcp.c:1301 tcp_cleanup_rbuf+0x4f/0x110() WARN(skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq), "cleanup rbuf bug: copied %X seq %X rcvnxt %X\n", tp->copied_seq, TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt); It appears TCP coalescing, and more specifically commit b081f85c297 (net: implement tcp coalescing in tcp_queue_rcv()) should take care of possible segment overlaps in receive queue. This was properly done in the case of out_or_order_queue by the caller. For example, segment at tail of queue have sequence 1000-2000, and we add a segment with sequence 1500-2500. This can happen in case of retransmits. In this case, just don't do the coalescing. Reported-by: Sergio Correia Signed-off-by: Eric Dumazet Tested-by: Sergio Correia Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'net/ipv4') diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index cfa2aa128342..b224eb8bce8b 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4555,6 +4555,11 @@ static bool tcp_try_coalesce(struct sock *sk, if (tcp_hdr(from)->fin) return false; + + /* Its possible this segment overlaps with prior segment in queue */ + if (TCP_SKB_CB(from)->seq != TCP_SKB_CB(to)->end_seq) + return false; + if (!skb_try_coalesce(to, from, fragstolen, &delta)) return false; -- cgit v1.2.3-59-g8ed1b