diff options
Diffstat (limited to 'net/ipv4/tcp_output.c')
-rw-r--r-- | net/ipv4/tcp_output.c | 20 |
1 files changed, 19 insertions, 1 deletions
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 8c8d7e06b72f..2ade67b7cdb0 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c | |||
@@ -2812,6 +2812,21 @@ begin_fwd: | |||
2812 | } | 2812 | } |
2813 | } | 2813 | } |
2814 | 2814 | ||
2815 | /* We allow to exceed memory limits for FIN packets to expedite | ||
2816 | * connection tear down and (memory) recovery. | ||
2817 | * Otherwise tcp_send_fin() could loop forever. | ||
2818 | */ | ||
2819 | static void sk_forced_wmem_schedule(struct sock *sk, int size) | ||
2820 | { | ||
2821 | int amt, status; | ||
2822 | |||
2823 | if (size <= sk->sk_forward_alloc) | ||
2824 | return; | ||
2825 | amt = sk_mem_pages(size); | ||
2826 | sk->sk_forward_alloc += amt * SK_MEM_QUANTUM; | ||
2827 | sk_memory_allocated_add(sk, amt, &status); | ||
2828 | } | ||
2829 | |||
2815 | /* Send a fin. The caller locks the socket for us. This cannot be | 2830 | /* Send a fin. The caller locks the socket for us. This cannot be |
2816 | * allowed to fail queueing a FIN frame under any circumstances. | 2831 | * allowed to fail queueing a FIN frame under any circumstances. |
2817 | */ | 2832 | */ |
@@ -2834,11 +2849,14 @@ void tcp_send_fin(struct sock *sk) | |||
2834 | } else { | 2849 | } else { |
2835 | /* Socket is locked, keep trying until memory is available. */ | 2850 | /* Socket is locked, keep trying until memory is available. */ |
2836 | for (;;) { | 2851 | for (;;) { |
2837 | skb = sk_stream_alloc_skb(sk, 0, sk->sk_allocation); | 2852 | skb = alloc_skb_fclone(MAX_TCP_HEADER, |
2853 | sk->sk_allocation); | ||
2838 | if (skb) | 2854 | if (skb) |
2839 | break; | 2855 | break; |
2840 | yield(); | 2856 | yield(); |
2841 | } | 2857 | } |
2858 | skb_reserve(skb, MAX_TCP_HEADER); | ||
2859 | sk_forced_wmem_schedule(sk, skb->truesize); | ||
2842 | /* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */ | 2860 | /* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */ |
2843 | tcp_init_nondata_skb(skb, tp->write_seq, | 2861 | tcp_init_nondata_skb(skb, tp->write_seq, |
2844 | TCPHDR_ACK | TCPHDR_FIN); | 2862 | TCPHDR_ACK | TCPHDR_FIN); |