Commit c9c33212 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

tcp: add tcp_add_backlog()

When TCP operates in lossy environments (between 1 and 10 % packet
losses), many SACK blocks can be exchanged, and I noticed we could
drop them on busy senders, if these SACK blocks have to be queued
into the socket backlog.

While the main cause is the poor performance of RACK/SACK processing,
we can try to avoid these drops of valuable information that can lead to
spurious timeouts and retransmits.

Cause of the drops is the skb->truesize overestimation caused by :

- drivers allocating ~2048 (or more) bytes as a fragment to hold an
  Ethernet frame.

- various pskb_may_pull() calls bringing the headers into skb->head
  might have pulled all the frame content, but skb->truesize could
  not be lowered, as the stack has no idea of each fragment truesize.

The backlog drops are also more visible on bidirectional flows, since
their sk_rmem_alloc can be quite big.

Let's add some room for the backlog, as only the socket owner
can selectively take action to lower memory needs, like collapsing
receive queues or partial ofo pruning.
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Cc: Yuchung Cheng <ycheng@google.com>
Cc: Neal Cardwell <ncardwell@google.com>
Acked-by: default avatarNeal Cardwell <ncardwell@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1a8ff8f5
...@@ -1161,6 +1161,7 @@ static inline void tcp_prequeue_init(struct tcp_sock *tp) ...@@ -1161,6 +1161,7 @@ static inline void tcp_prequeue_init(struct tcp_sock *tp)
} }
bool tcp_prequeue(struct sock *sk, struct sk_buff *skb); bool tcp_prequeue(struct sock *sk, struct sk_buff *skb);
bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb);
#undef STATE_TRACE #undef STATE_TRACE
......
...@@ -1532,6 +1532,34 @@ bool tcp_prequeue(struct sock *sk, struct sk_buff *skb) ...@@ -1532,6 +1532,34 @@ bool tcp_prequeue(struct sock *sk, struct sk_buff *skb)
} }
EXPORT_SYMBOL(tcp_prequeue); EXPORT_SYMBOL(tcp_prequeue);
bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
{
u32 limit = sk->sk_rcvbuf + sk->sk_sndbuf;
/* Only socket owner can try to collapse/prune rx queues
* to reduce memory overhead, so add a little headroom here.
* Few sockets backlog are possibly concurrently non empty.
*/
limit += 64*1024;
/* In case all data was pulled from skb frags (in __pskb_pull_tail()),
* we can fix skb->truesize to its real value to avoid future drops.
* This is valid because skb is not yet charged to the socket.
* It has been noticed pure SACK packets were sometimes dropped
* (if cooked by drivers without copybreak feature).
*/
if (!skb->data_len)
skb->truesize = SKB_TRUESIZE(skb_end_offset(skb));
if (unlikely(sk_add_backlog(sk, skb, limit))) {
bh_unlock_sock(sk);
__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPBACKLOGDROP);
return true;
}
return false;
}
EXPORT_SYMBOL(tcp_add_backlog);
/* /*
* From tcp_input.c * From tcp_input.c
*/ */
...@@ -1662,10 +1690,7 @@ int tcp_v4_rcv(struct sk_buff *skb) ...@@ -1662,10 +1690,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
if (!sock_owned_by_user(sk)) { if (!sock_owned_by_user(sk)) {
if (!tcp_prequeue(sk, skb)) if (!tcp_prequeue(sk, skb))
ret = tcp_v4_do_rcv(sk, skb); ret = tcp_v4_do_rcv(sk, skb);
} else if (unlikely(sk_add_backlog(sk, skb, } else if (tcp_add_backlog(sk, skb)) {
sk->sk_rcvbuf + sk->sk_sndbuf))) {
bh_unlock_sock(sk);
__NET_INC_STATS(net, LINUX_MIB_TCPBACKLOGDROP);
goto discard_and_relse; goto discard_and_relse;
} }
bh_unlock_sock(sk); bh_unlock_sock(sk);
......
...@@ -1467,10 +1467,7 @@ static int tcp_v6_rcv(struct sk_buff *skb) ...@@ -1467,10 +1467,7 @@ static int tcp_v6_rcv(struct sk_buff *skb)
if (!sock_owned_by_user(sk)) { if (!sock_owned_by_user(sk)) {
if (!tcp_prequeue(sk, skb)) if (!tcp_prequeue(sk, skb))
ret = tcp_v6_do_rcv(sk, skb); ret = tcp_v6_do_rcv(sk, skb);
} else if (unlikely(sk_add_backlog(sk, skb, } else if (tcp_add_backlog(sk, skb)) {
sk->sk_rcvbuf + sk->sk_sndbuf))) {
bh_unlock_sock(sk);
__NET_INC_STATS(net, LINUX_MIB_TCPBACKLOGDROP);
goto discard_and_relse; goto discard_and_relse;
} }
bh_unlock_sock(sk); bh_unlock_sock(sk);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment