Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit af2806f8 authored by Thomas Graf's avatar Thomas Graf Committed by Jesse Gross
Browse files

net: Export skb_zerocopy() to zerocopy from one skb to another



Make the skb zerocopy logic written for nfnetlink queue available for
use by other modules.

Signed-off-by: default avatarThomas Graf <tgraf@suug.ch>
Reviewed-by: default avatarDaniel Borkmann <dborkman@redhat.com>
Acked-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarJesse Gross <jesse@nicira.com>
parent 5f03f47c
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -2345,6 +2345,9 @@ int skb_splice_bits(struct sk_buff *skb, unsigned int offset,
		    struct pipe_inode_info *pipe, unsigned int len,
		    struct pipe_inode_info *pipe, unsigned int len,
		    unsigned int flags);
		    unsigned int flags);
void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
unsigned int skb_zerocopy_headlen(const struct sk_buff *from);
void skb_zerocopy(struct sk_buff *to, const struct sk_buff *from,
		  int len, int hlen);
void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len);
void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len);
int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen);
int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen);
void skb_scrub_packet(struct sk_buff *skb, bool xnet);
void skb_scrub_packet(struct sk_buff *skb, bool xnet);
+85 −0
Original line number Original line Diff line number Diff line
@@ -2122,6 +2122,91 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
}
}
EXPORT_SYMBOL(skb_copy_and_csum_bits);
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)
void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
{
{
	__wsum csum;
	__wsum csum;
+4 −55
Original line number Original line Diff line number Diff line
@@ -235,51 +235,6 @@ nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data)
	spin_unlock_bh(&queue->lock);
	spin_unlock_bh(&queue->lock);
}
}


static void
nfqnl_zcopy(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;

	/* 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;
}

static int
static int
nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet,
nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet,
		      bool csum_verify)
		      bool csum_verify)
@@ -304,7 +259,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
{
{
	size_t size;
	size_t size;
	size_t data_len = 0, cap_len = 0;
	size_t data_len = 0, cap_len = 0;
	int hlen = 0;
	unsigned int hlen = 0;
	struct sk_buff *skb;
	struct sk_buff *skb;
	struct nlattr *nla;
	struct nlattr *nla;
	struct nfqnl_msg_packet_hdr *pmsg;
	struct nfqnl_msg_packet_hdr *pmsg;
@@ -356,14 +311,8 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
		if (data_len > entskb->len)
		if (data_len > entskb->len)
			data_len = entskb->len;
			data_len = entskb->len;


		if (!entskb->head_frag ||
		hlen = skb_zerocopy_headlen(entskb);
		    skb_headlen(entskb) < L1_CACHE_BYTES ||
		hlen = min_t(unsigned int, hlen, data_len);
		    skb_shinfo(entskb)->nr_frags >= MAX_SKB_FRAGS)
			hlen = skb_headlen(entskb);

		if (skb_has_frag_list(entskb))
			hlen = entskb->len;
		hlen = min_t(int, data_len, hlen);
		size += sizeof(struct nlattr) + hlen;
		size += sizeof(struct nlattr) + hlen;
		cap_len = entskb->len;
		cap_len = entskb->len;
		break;
		break;
@@ -504,7 +453,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
		nla->nla_type = NFQA_PAYLOAD;
		nla->nla_type = NFQA_PAYLOAD;
		nla->nla_len = nla_attr_size(data_len);
		nla->nla_len = nla_attr_size(data_len);


		nfqnl_zcopy(skb, entskb, data_len, hlen);
		skb_zerocopy(skb, entskb, data_len, hlen);
	}
	}


	nlh->nlmsg_len = skb->len;
	nlh->nlmsg_len = skb->len;