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

Commit 2870c43d authored by Octavian Purdila's avatar Octavian Purdila Committed by David S. Miller
Browse files

net: refactor tcp splice receive path to improve readability



- move all of the details on offsets, lengths and buffers into a
single function instead of doing these operation from multiple places

- use a bottom up approach: try to avoid details in the high level
functions, introduce them gradually as we go deeper in the function
call stack

With helpful feedback from Jarek Poplawski.

Signed-off-by: default avatarOctavian Purdila <opurdila@ixiacom.com>
Acked-by: default avatarJarek Poplawski <jarkao2@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b9e40857
Loading
Loading
Loading
Loading
+61 −92
Original line number Diff line number Diff line
@@ -1282,115 +1282,84 @@ static inline int spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
	return 0;
}

/*
 * Map linear and fragment data from the skb to spd. Returns number of
 * pages mapped.
 */
static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset,
			     unsigned int *total_len,
static inline void __segment_seek(struct page **page, unsigned int *poff,
				  unsigned int *plen, unsigned int off)
{
	*poff += off;
	*page += *poff / PAGE_SIZE;
	*poff = *poff % PAGE_SIZE;
	*plen -= off;
}

static inline int __splice_segment(struct page *page, unsigned int poff,
				   unsigned int plen, unsigned int *off,
				   unsigned int *len, struct sk_buff *skb,
				   struct splice_pipe_desc *spd)
{
	unsigned int nr_pages = spd->nr_pages;
	unsigned int poff, plen, len, toff, tlen;
	int headlen, seg, error = 0;
	if (!*len)
		return 1;

	toff = *offset;
	tlen = *total_len;
	if (!tlen) {
		error = 1;
		goto err;
	/* skip this segment if already processed */
	if (*off >= plen) {
		*off -= plen;
		return 0;
	}

	/*
	 * if the offset is greater than the linear part, go directly to
	 * the fragments.
	 */
	headlen = skb_headlen(skb);
	if (toff >= headlen) {
		toff -= headlen;
		goto map_frag;
	/* ignore any bits we already processed */
	if (*off) {
		__segment_seek(&page, &poff, &plen, *off);
		*off = 0;
	}

	/*
	 * first map the linear region into the pages/partial map, skipping
	 * any potential initial offset.
	 */
	len = 0;
	while (len < headlen) {
		void *p = skb->data + len;
	do {
		unsigned int flen = min(*len, plen);

		poff = (unsigned long) p & (PAGE_SIZE - 1);
		plen = min_t(unsigned int, headlen - len, PAGE_SIZE - poff);
		len += plen;
		/* the linear region may spread across several pages  */
		flen = min_t(unsigned int, flen, PAGE_SIZE - poff);

		if (toff) {
			if (plen <= toff) {
				toff -= plen;
				continue;
			}
			plen -= toff;
			poff += toff;
			toff = 0;
		}
		if (spd_fill_page(spd, page, flen, poff, skb))
			return 1;

		plen = min(plen, tlen);
		if (!plen)
			break;
		__segment_seek(&page, &poff, &plen, flen);
		*len -= flen;

	} while (*len && plen);

	return 0;
}

/*
		 * just jump directly to update and return, no point
		 * in going over fragments when the output is full.
 * Map linear and fragment data from the skb to spd. It reports failure if the
 * pipe is full or if we already spliced the requested length.
 */
		error = spd_fill_page(spd, virt_to_page(p), plen, poff, skb);
		if (error)
			goto done;
int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset,
		      unsigned int *len,
		      struct splice_pipe_desc *spd)
{
	int seg;

		tlen -= plen;
	}
	/*
	 * map the linear part
	 */
	if (__splice_segment(virt_to_page(skb->data),
			     (unsigned long) skb->data & (PAGE_SIZE - 1),
			     skb_headlen(skb),
			     offset, len, skb, spd))
		return 1;

	/*
	 * then map the fragments
	 */
map_frag:
	for (seg = 0; seg < skb_shinfo(skb)->nr_frags; seg++) {
		const skb_frag_t *f = &skb_shinfo(skb)->frags[seg];

		plen = f->size;
		poff = f->page_offset;

		if (toff) {
			if (plen <= toff) {
				toff -= plen;
				continue;
			}
			plen -= toff;
			poff += toff;
			toff = 0;
		}

		plen = min(plen, tlen);
		if (!plen)
			break;

		error = spd_fill_page(spd, f->page, plen, poff, skb);
		if (error)
			break;

		tlen -= plen;
		if (__splice_segment(f->page, f->page_offset, f->size,
				     offset, len, skb, spd))
			return 1;
	}

done:
	if (spd->nr_pages - nr_pages) {
		*offset = 0;
		*total_len = tlen;
	return 0;
}
err:
	/* update the offset to reflect the linear part skip, if any */
	if (!error)
		*offset = toff;
	return error;
}

/*
 * Map data from the skb to a pipe. Should handle both the linear part,