diff options
author | Eric Dumazet <edumazet@google.com> | 2012-05-02 05:58:29 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-05-02 21:11:11 -0400 |
commit | b081f85c2977b1cbb6e635d53d9512f1ef985972 (patch) | |
tree | 3b41dfe3ee282c175907e8bcf9614385115fa2e6 /net/ipv4/tcp_input.c | |
parent | 923dd347b8904c24bcac89bf038ed4da87f8aa90 (diff) |
net: implement tcp coalescing in tcp_queue_rcv()
Extend tcp coalescing implementing it from tcp_queue_rcv(), the main
receiver function when application is not blocked in recvmsg().
Function tcp_queue_rcv() is moved a bit to allow its call from
tcp_data_queue()
This gives good results especially if GRO could not kick, and if skb
head is a fragment.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Alexander Duyck <alexander.h.duyck@intel.com>
Cc: Neal Cardwell <ncardwell@google.com>
Cc: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_input.c')
-rw-r--r-- | net/ipv4/tcp_input.c | 40 |
1 files changed, 21 insertions, 19 deletions
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a8829370f712..2f696ef13dcd 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c | |||
@@ -4739,6 +4739,22 @@ end: | |||
4739 | skb_set_owner_r(skb, sk); | 4739 | skb_set_owner_r(skb, sk); |
4740 | } | 4740 | } |
4741 | 4741 | ||
4742 | int tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen, | ||
4743 | bool *fragstolen) | ||
4744 | { | ||
4745 | int eaten; | ||
4746 | struct sk_buff *tail = skb_peek_tail(&sk->sk_receive_queue); | ||
4747 | |||
4748 | __skb_pull(skb, hdrlen); | ||
4749 | eaten = (tail && | ||
4750 | tcp_try_coalesce(sk, tail, skb, fragstolen)) ? 1 : 0; | ||
4751 | tcp_sk(sk)->rcv_nxt = TCP_SKB_CB(skb)->end_seq; | ||
4752 | if (!eaten) { | ||
4753 | __skb_queue_tail(&sk->sk_receive_queue, skb); | ||
4754 | skb_set_owner_r(skb, sk); | ||
4755 | } | ||
4756 | return eaten; | ||
4757 | } | ||
4742 | 4758 | ||
4743 | static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) | 4759 | static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) |
4744 | { | 4760 | { |
@@ -4785,20 +4801,12 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) | |||
4785 | } | 4801 | } |
4786 | 4802 | ||
4787 | if (eaten <= 0) { | 4803 | if (eaten <= 0) { |
4788 | struct sk_buff *tail; | ||
4789 | queue_and_out: | 4804 | queue_and_out: |
4790 | if (eaten < 0 && | 4805 | if (eaten < 0 && |
4791 | tcp_try_rmem_schedule(sk, skb->truesize)) | 4806 | tcp_try_rmem_schedule(sk, skb->truesize)) |
4792 | goto drop; | 4807 | goto drop; |
4793 | 4808 | ||
4794 | tail = skb_peek_tail(&sk->sk_receive_queue); | 4809 | eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen); |
4795 | eaten = (tail && | ||
4796 | tcp_try_coalesce(sk, tail, skb, | ||
4797 | &fragstolen)) ? 1 : 0; | ||
4798 | if (eaten <= 0) { | ||
4799 | skb_set_owner_r(skb, sk); | ||
4800 | __skb_queue_tail(&sk->sk_receive_queue, skb); | ||
4801 | } | ||
4802 | } | 4810 | } |
4803 | tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; | 4811 | tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; |
4804 | if (skb->len) | 4812 | if (skb->len) |
@@ -5493,14 +5501,6 @@ discard: | |||
5493 | return 0; | 5501 | return 0; |
5494 | } | 5502 | } |
5495 | 5503 | ||
5496 | void tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen) | ||
5497 | { | ||
5498 | __skb_pull(skb, hdrlen); | ||
5499 | __skb_queue_tail(&sk->sk_receive_queue, skb); | ||
5500 | skb_set_owner_r(skb, sk); | ||
5501 | tcp_sk(sk)->rcv_nxt = TCP_SKB_CB(skb)->end_seq; | ||
5502 | } | ||
5503 | |||
5504 | /* | 5504 | /* |
5505 | * TCP receive function for the ESTABLISHED state. | 5505 | * TCP receive function for the ESTABLISHED state. |
5506 | * | 5506 | * |
@@ -5609,6 +5609,7 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, | |||
5609 | } else { | 5609 | } else { |
5610 | int eaten = 0; | 5610 | int eaten = 0; |
5611 | int copied_early = 0; | 5611 | int copied_early = 0; |
5612 | bool fragstolen = false; | ||
5612 | 5613 | ||
5613 | if (tp->copied_seq == tp->rcv_nxt && | 5614 | if (tp->copied_seq == tp->rcv_nxt && |
5614 | len - tcp_header_len <= tp->ucopy.len) { | 5615 | len - tcp_header_len <= tp->ucopy.len) { |
@@ -5666,7 +5667,8 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, | |||
5666 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPHPHITS); | 5667 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPHPHITS); |
5667 | 5668 | ||
5668 | /* Bulk data transfer: receiver */ | 5669 | /* Bulk data transfer: receiver */ |
5669 | tcp_queue_rcv(sk, skb, tcp_header_len); | 5670 | eaten = tcp_queue_rcv(sk, skb, tcp_header_len, |
5671 | &fragstolen); | ||
5670 | } | 5672 | } |
5671 | 5673 | ||
5672 | tcp_event_data_recv(sk, skb); | 5674 | tcp_event_data_recv(sk, skb); |
@@ -5688,7 +5690,7 @@ no_ack: | |||
5688 | else | 5690 | else |
5689 | #endif | 5691 | #endif |
5690 | if (eaten) | 5692 | if (eaten) |
5691 | __kfree_skb(skb); | 5693 | kfree_skb_partial(skb, fragstolen); |
5692 | else | 5694 | else |
5693 | sk->sk_data_ready(sk, 0); | 5695 | sk->sk_data_ready(sk, 0); |
5694 | return 0; | 5696 | return 0; |