|
@@ -932,17 +932,20 @@ EXPORT_SYMBOL_GPL(skb_morph);
|
|
|
*/
|
|
|
int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask)
|
|
|
{
|
|
|
- int i;
|
|
|
+ struct ubuf_info *uarg = skb_shinfo(skb)->destructor_arg;
|
|
|
int num_frags = skb_shinfo(skb)->nr_frags;
|
|
|
struct page *page, *head = NULL;
|
|
|
- struct ubuf_info *uarg = skb_shinfo(skb)->destructor_arg;
|
|
|
+ int i, new_frags;
|
|
|
+ u32 d_off;
|
|
|
|
|
|
- for (i = 0; i < num_frags; i++) {
|
|
|
- skb_frag_t *f = &skb_shinfo(skb)->frags[i];
|
|
|
- u32 p_off, p_len, copied;
|
|
|
- struct page *p;
|
|
|
- u8 *vaddr;
|
|
|
+ if (!num_frags)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (skb_shared(skb) || skb_unclone(skb, gfp_mask))
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
+ new_frags = (__skb_pagelen(skb) + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
|
+ for (i = 0; i < new_frags; i++) {
|
|
|
page = alloc_page(gfp_mask);
|
|
|
if (!page) {
|
|
|
while (head) {
|
|
@@ -952,17 +955,36 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask)
|
|
|
}
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
+ set_page_private(page, (unsigned long)head);
|
|
|
+ head = page;
|
|
|
+ }
|
|
|
+
|
|
|
+ page = head;
|
|
|
+ d_off = 0;
|
|
|
+ for (i = 0; i < num_frags; i++) {
|
|
|
+ skb_frag_t *f = &skb_shinfo(skb)->frags[i];
|
|
|
+ u32 p_off, p_len, copied;
|
|
|
+ struct page *p;
|
|
|
+ u8 *vaddr;
|
|
|
|
|
|
skb_frag_foreach_page(f, f->page_offset, skb_frag_size(f),
|
|
|
p, p_off, p_len, copied) {
|
|
|
+ u32 copy, done = 0;
|
|
|
vaddr = kmap_atomic(p);
|
|
|
- memcpy(page_address(page) + copied, vaddr + p_off,
|
|
|
- p_len);
|
|
|
+
|
|
|
+ while (done < p_len) {
|
|
|
+ if (d_off == PAGE_SIZE) {
|
|
|
+ d_off = 0;
|
|
|
+ page = (struct page *)page_private(page);
|
|
|
+ }
|
|
|
+ copy = min_t(u32, PAGE_SIZE - d_off, p_len - done);
|
|
|
+ memcpy(page_address(page) + d_off,
|
|
|
+ vaddr + p_off + done, copy);
|
|
|
+ done += copy;
|
|
|
+ d_off += copy;
|
|
|
+ }
|
|
|
kunmap_atomic(vaddr);
|
|
|
}
|
|
|
-
|
|
|
- set_page_private(page, (unsigned long)head);
|
|
|
- head = page;
|
|
|
}
|
|
|
|
|
|
/* skb frags release userspace buffers */
|
|
@@ -972,11 +994,12 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask)
|
|
|
uarg->callback(uarg, false);
|
|
|
|
|
|
/* skb frags point to kernel buffers */
|
|
|
- for (i = num_frags - 1; i >= 0; i--) {
|
|
|
- __skb_fill_page_desc(skb, i, head, 0,
|
|
|
- skb_shinfo(skb)->frags[i].size);
|
|
|
+ for (i = 0; i < new_frags - 1; i++) {
|
|
|
+ __skb_fill_page_desc(skb, i, head, 0, PAGE_SIZE);
|
|
|
head = (struct page *)page_private(head);
|
|
|
}
|
|
|
+ __skb_fill_page_desc(skb, new_frags - 1, head, 0, d_off);
|
|
|
+ skb_shinfo(skb)->nr_frags = new_frags;
|
|
|
|
|
|
skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY;
|
|
|
return 0;
|