diff options
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/tcp_output.c | 26 |
1 files changed, 19 insertions, 7 deletions
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 0a5d97c20aa9..e13d77857225 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c | |||
@@ -839,26 +839,38 @@ void tcp_wfree(struct sk_buff *skb) | |||
839 | { | 839 | { |
840 | struct sock *sk = skb->sk; | 840 | struct sock *sk = skb->sk; |
841 | struct tcp_sock *tp = tcp_sk(sk); | 841 | struct tcp_sock *tp = tcp_sk(sk); |
842 | int wmem; | ||
843 | |||
844 | /* Keep one reference on sk_wmem_alloc. | ||
845 | * Will be released by sk_free() from here or tcp_tasklet_func() | ||
846 | */ | ||
847 | wmem = atomic_sub_return(skb->truesize - 1, &sk->sk_wmem_alloc); | ||
848 | |||
849 | /* If this softirq is serviced by ksoftirqd, we are likely under stress. | ||
850 | * Wait until our queues (qdisc + devices) are drained. | ||
851 | * This gives : | ||
852 | * - less callbacks to tcp_write_xmit(), reducing stress (batches) | ||
853 | * - chance for incoming ACK (processed by another cpu maybe) | ||
854 | * to migrate this flow (skb->ooo_okay will be eventually set) | ||
855 | */ | ||
856 | if (wmem >= SKB_TRUESIZE(1) && this_cpu_ksoftirqd() == current) | ||
857 | goto out; | ||
842 | 858 | ||
843 | if (test_and_clear_bit(TSQ_THROTTLED, &tp->tsq_flags) && | 859 | if (test_and_clear_bit(TSQ_THROTTLED, &tp->tsq_flags) && |
844 | !test_and_set_bit(TSQ_QUEUED, &tp->tsq_flags)) { | 860 | !test_and_set_bit(TSQ_QUEUED, &tp->tsq_flags)) { |
845 | unsigned long flags; | 861 | unsigned long flags; |
846 | struct tsq_tasklet *tsq; | 862 | struct tsq_tasklet *tsq; |
847 | 863 | ||
848 | /* Keep a ref on socket. | ||
849 | * This last ref will be released in tcp_tasklet_func() | ||
850 | */ | ||
851 | atomic_sub(skb->truesize - 1, &sk->sk_wmem_alloc); | ||
852 | |||
853 | /* queue this socket to tasklet queue */ | 864 | /* queue this socket to tasklet queue */ |
854 | local_irq_save(flags); | 865 | local_irq_save(flags); |
855 | tsq = &__get_cpu_var(tsq_tasklet); | 866 | tsq = &__get_cpu_var(tsq_tasklet); |
856 | list_add(&tp->tsq_node, &tsq->head); | 867 | list_add(&tp->tsq_node, &tsq->head); |
857 | tasklet_schedule(&tsq->tasklet); | 868 | tasklet_schedule(&tsq->tasklet); |
858 | local_irq_restore(flags); | 869 | local_irq_restore(flags); |
859 | } else { | 870 | return; |
860 | sock_wfree(skb); | ||
861 | } | 871 | } |
872 | out: | ||
873 | sk_free(sk); | ||
862 | } | 874 | } |
863 | 875 | ||
864 | /* This routine actually transmits TCP packets queued in by | 876 | /* This routine actually transmits TCP packets queued in by |