Once tcp_collapse() finds a collapsible skb, it collapses consecutive bloated skbs. However, the end result could be the same or larger than as is in some cases: 1. skb is already minimum If SKB_HEAD_ALIGN(len) <= SKB_SMALL_HEAD_CACHE_SIZE, skb is allocated from skbuff_small_head, and skb->truesize never changes. 2. SKB_HEAD_ALIGN() and kmalloc_size_roundup() in alloc_skb() Assume two bloated skbs with len = 1448 B (truesize = 2304 B, scaling_ratio = 160) and len = 300 B (truesize = 896 B, scaling_ratio = 85), whose total truesize is 3200 B. Collapsing them calls alloc_skb(1748), which rounds up SKB_HEAD_ALIGN(1748) = 2112 B to 4096 B and allocates a new skb with truesize = 4352 B (+1152 B!). Even worse, in case 1, such a skb is reallocated repeatedly by every tcp_collapse() in vain. Let's calculate the final collapsed truesize before collapsing skbs and skip the range if it does not shrink. We could allocate skbs first and calculate the new truesize, but given tcp_collapse() is called under memory pressure, we want to avoid unnecessary allocation. Signed-off-by: Kuniyuki Iwashima --- include/linux/skbuff.h | 7 +++++++ net/ipv4/tcp_input.c | 24 +++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d36fbb88b3f6..618e629df15a 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -292,6 +292,13 @@ SKB_DATA_ALIGN(sizeof(struct sk_buff)) + \ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) +/* return estimated truesize allocated by alloc_skb(X) via kmalloc_reserve() */ +#define SKB_ALLOCSIZE(X) \ + ((SKB_HEAD_ALIGN(X) <= SKB_SMALL_HEAD_CACHE_SIZE ? \ + SKB_SMALL_HEAD_CACHE_SIZE : \ + kmalloc_size_roundup(SKB_HEAD_ALIGN(X))) + \ + SKB_DATA_ALIGN(sizeof(struct sk_buff))) + struct net_device; struct scatterlist; struct pipe_inode_info; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 5bd42a74c6f5..fe71305f6e67 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5850,8 +5850,10 @@ static void tcp_collapse(struct sock *sk, struct sk_buff_head *list, struct rb_root *root, struct sk_buff *head, struct sk_buff *tail, u32 start) { + int old_truesize, new_truesize; struct sk_buff *skb = head, *n; struct sk_buff_head tmp; + int remainder, nskbs; u8 scaling_ratio; u32 sub_end; @@ -5891,6 +5893,7 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list, struct rb_root *root, /* Second, find consecutive collapsible skb. */ sub_end = TCP_SKB_CB(skb)->end_seq; + old_truesize = skb->truesize; for (n = tcp_skb_next(skb, list); n && n != tail; n = tcp_skb_next(n, list)) { if (!skb_frags_readable(n) || @@ -5898,8 +5901,10 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list, struct rb_root *root, !tcp_skb_can_collapse_rx(skb, n)) break; - if (!after(TCP_SKB_CB(n)->end_seq, sub_end)) + if (!after(TCP_SKB_CB(n)->end_seq, sub_end)) { + old_truesize += n->truesize; continue; + } /* seq and end_seq must increase monotonically for rbtree. */ if ((list || !before(TCP_SKB_CB(n)->seq, sub_end)) && @@ -5908,6 +5913,23 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list, struct rb_root *root, break; sub_end = TCP_SKB_CB(n)->end_seq; + old_truesize += n->truesize; + } + + /* Third, check if the new skbs have high density. */ + nskbs = (sub_end - start) / TCP_COLLAPSE_MAX_SIZE; + new_truesize = nskbs * SKB_TRUESIZE(TCP_COLLAPSE_MAX_SIZE); + + remainder = (sub_end - start) % TCP_COLLAPSE_MAX_SIZE; + if (remainder) { + nskbs++; + new_truesize += SKB_ALLOCSIZE(remainder); + } + + if (old_truesize <= new_truesize) { + start = sub_end; + skb = n; + goto restart; } while (before(start, sub_end)) { -- 2.56.0.611.g1030f0b940-goog