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

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
...@@ -1282,114 +1282,83 @@ static inline int spd_fill_page(struct splice_pipe_desc *spd, struct page *page, ...@@ -1282,114 +1282,83 @@ static inline int spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
return 0; return 0;
} }
/* static inline void __segment_seek(struct page **page, unsigned int *poff,
* Map linear and fragment data from the skb to spd. Returns number of unsigned int *plen, unsigned int off)
* pages mapped. {
*/ *poff += off;
static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset, *page += *poff / PAGE_SIZE;
unsigned int *total_len, *poff = *poff % PAGE_SIZE;
struct splice_pipe_desc *spd) *plen -= off;
{ }
unsigned int nr_pages = spd->nr_pages;
unsigned int poff, plen, len, toff, tlen; static inline int __splice_segment(struct page *page, unsigned int poff,
int headlen, seg, error = 0; unsigned int plen, unsigned int *off,
unsigned int *len, struct sk_buff *skb,
toff = *offset; struct splice_pipe_desc *spd)
tlen = *total_len; {
if (!tlen) { if (!*len)
error = 1; return 1;
goto err;
/* skip this segment if already processed */
if (*off >= plen) {
*off -= plen;
return 0;
} }
/* /* ignore any bits we already processed */
* if the offset is greater than the linear part, go directly to if (*off) {
* the fragments. __segment_seek(&page, &poff, &plen, *off);
*/ *off = 0;
headlen = skb_headlen(skb);
if (toff >= headlen) {
toff -= headlen;
goto map_frag;
} }
/* do {
* first map the linear region into the pages/partial map, skipping unsigned int flen = min(*len, plen);
* any potential initial offset.
*/
len = 0;
while (len < headlen) {
void *p = skb->data + len;
poff = (unsigned long) p & (PAGE_SIZE - 1);
plen = min_t(unsigned int, headlen - len, PAGE_SIZE - poff);
len += plen;
if (toff) {
if (plen <= toff) {
toff -= plen;
continue;
}
plen -= toff;
poff += toff;
toff = 0;
}
plen = min(plen, tlen); /* the linear region may spread across several pages */
if (!plen) flen = min_t(unsigned int, flen, PAGE_SIZE - poff);
break;
/* if (spd_fill_page(spd, page, flen, poff, skb))
* just jump directly to update and return, no point return 1;
* in going over fragments when the output is full.
*/
error = spd_fill_page(spd, virt_to_page(p), plen, poff, skb);
if (error)
goto done;
tlen -= plen; __segment_seek(&page, &poff, &plen, flen);
} *len -= flen;
} while (*len && plen);
return 0;
}
/*
* 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.
*/
int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset,
unsigned int *len,
struct splice_pipe_desc *spd)
{
int seg;
/*
* 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 * then map the fragments
*/ */
map_frag:
for (seg = 0; seg < skb_shinfo(skb)->nr_frags; seg++) { for (seg = 0; seg < skb_shinfo(skb)->nr_frags; seg++) {
const skb_frag_t *f = &skb_shinfo(skb)->frags[seg]; const skb_frag_t *f = &skb_shinfo(skb)->frags[seg];
plen = f->size; if (__splice_segment(f->page, f->page_offset, f->size,
poff = f->page_offset; offset, len, skb, spd))
return 1;
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;
} }
done: return 0;
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;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment