|
@@ -1282,114 +1282,83 @@ static inline int spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
|
|
return 0;
|
|
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,
|
|
|
|
- struct splice_pipe_desc *spd)
|
|
|
|
-{
|
|
|
|
- unsigned int nr_pages = spd->nr_pages;
|
|
|
|
- unsigned int poff, plen, len, toff, tlen;
|
|
|
|
- int headlen, seg, error = 0;
|
|
|
|
-
|
|
|
|
- toff = *offset;
|
|
|
|
- tlen = *total_len;
|
|
|
|
- if (!tlen) {
|
|
|
|
- error = 1;
|
|
|
|
- goto err;
|
|
|
|
|
|
+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)
|
|
|
|
+{
|
|
|
|
+ if (!*len)
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ /* 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;
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
- }
|
|
|
|
|
|
+ do {
|
|
|
|
+ unsigned int flen = min(*len, plen);
|
|
|
|
|
|
- plen = min(plen, tlen);
|
|
|
|
- if (!plen)
|
|
|
|
- break;
|
|
|
|
|
|
+ /* the linear region may spread across several pages */
|
|
|
|
+ flen = min_t(unsigned int, flen, PAGE_SIZE - poff);
|
|
|
|
|
|
- /*
|
|
|
|
- * just jump directly to update and return, no point
|
|
|
|
- * 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;
|
|
|
|
|
|
+ if (spd_fill_page(spd, page, flen, poff, skb))
|
|
|
|
+ return 1;
|
|
|
|
|
|
- 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;
|
|
|
|
- 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;
|
|
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|