diff options
| -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 |
