diff options
Diffstat (limited to 'net/core/skbuff.c')
| -rw-r--r-- | net/core/skbuff.c | 85 | 
1 files changed, 85 insertions, 0 deletions
| diff --git a/net/core/skbuff.c b/net/core/skbuff.c index eb96c2c22400..1d641e781f85 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2121,6 +2121,91 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,  }  EXPORT_SYMBOL(skb_copy_and_csum_bits); + /** + *	skb_zerocopy_headlen - Calculate headroom needed for skb_zerocopy() + *	@from: source buffer + * + *	Calculates the amount of linear headroom needed in the 'to' skb passed + *	into skb_zerocopy(). + */ +unsigned int +skb_zerocopy_headlen(const struct sk_buff *from) +{ +	unsigned int hlen = 0; + +	if (!from->head_frag || +	    skb_headlen(from) < L1_CACHE_BYTES || +	    skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS) +		hlen = skb_headlen(from); + +	if (skb_has_frag_list(from)) +		hlen = from->len; + +	return hlen; +} +EXPORT_SYMBOL_GPL(skb_zerocopy_headlen); + +/** + *	skb_zerocopy - Zero copy skb to skb + *	@to: destination buffer + *	@source: source buffer + *	@len: number of bytes to copy from source buffer + *	@hlen: size of linear headroom in destination buffer + * + *	Copies up to `len` bytes from `from` to `to` by creating references + *	to the frags in the source buffer. + * + *	The `hlen` as calculated by skb_zerocopy_headlen() specifies the + *	headroom in the `to` buffer. + */ +void +skb_zerocopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen) +{ +	int i, j = 0; +	int plen = 0; /* length of skb->head fragment */ +	struct page *page; +	unsigned int offset; + +	BUG_ON(!from->head_frag && !hlen); + +	/* dont bother with small payloads */ +	if (len <= skb_tailroom(to)) { +		skb_copy_bits(from, 0, skb_put(to, len), len); +		return; +	} + +	if (hlen) { +		skb_copy_bits(from, 0, skb_put(to, hlen), hlen); +		len -= hlen; +	} else { +		plen = min_t(int, skb_headlen(from), len); +		if (plen) { +			page = virt_to_head_page(from->head); +			offset = from->data - (unsigned char *)page_address(page); +			__skb_fill_page_desc(to, 0, page, offset, plen); +			get_page(page); +			j = 1; +			len -= plen; +		} +	} + +	to->truesize += len + plen; +	to->len += len + plen; +	to->data_len += len + plen; + +	for (i = 0; i < skb_shinfo(from)->nr_frags; i++) { +		if (!len) +			break; +		skb_shinfo(to)->frags[j] = skb_shinfo(from)->frags[i]; +		skb_shinfo(to)->frags[j].size = min_t(int, skb_shinfo(to)->frags[j].size, len); +		len -= skb_shinfo(to)->frags[j].size; +		skb_frag_ref(to, j); +		j++; +	} +	skb_shinfo(to)->nr_frags = j; +} +EXPORT_SYMBOL_GPL(skb_zerocopy); +  void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)  {  	__wsum csum; | 
