diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 52ba2b5e803d..9e8caa0c4f72 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1699,17 +1699,18 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe, struct splice_pipe_desc *spd, struct sock *sk) { int seg; - bool head_is_linear = !skb->head_frag; + bool head_is_locked = !skb->head_frag || skb_cloned(skb); /* map the linear part : - * If skb->head_frag is set, this 'linear' part is backed - * by a fragment, and we can avoid a copy. + * If skb->head_frag is set, this 'linear' part is backed by a + * fragment, and if the head is not shared with any clones then + * we can avoid a copy since we own the head portion of this page. */ if (__splice_segment(virt_to_page(skb->data), (unsigned long) skb->data & (PAGE_SIZE - 1), skb_headlen(skb), offset, len, skb, spd, - head_is_linear, + head_is_locked, sk, pipe)) return true; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2f696ef13dcd..c6f78e2b590f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4589,7 +4589,7 @@ copyfrags: to->data_len += len; goto merge; } - if (from->head_frag) { + if (from->head_frag && !skb_cloned(from)) { struct page *page; unsigned int offset; @@ -4599,12 +4599,7 @@ copyfrags: offset = from->data - (unsigned char *)page_address(page); skb_fill_page_desc(to, skb_shinfo(to)->nr_frags, page, offset, skb_headlen(from)); - - if (skb_cloned(from)) - get_page(page); - else - *fragstolen = true; - + *fragstolen = true; delta = len; /* we dont know real truesize... */ goto copyfrags; }