This patch avoids the correctness issue on the user-space mapping
by just copying the memory.
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 4c15dc4..d75cfd2 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -62,6 +62,7 @@
#include <linux/if_ether.h>
#include <linux/if_tun.h>
#include <linux/crc32.h>
+#include <linux/highmem.h>
#include <linux/virtio_net.h>
#include <net/net_namespace.h>
@@ -257,65 +258,55 @@ static struct sk_buff *copy_user_skb(size_t align, struct
iovec *iv, size_t len)
}
/* This will fail if they give us a crazy iovec, but that's their own fault. */
-static int get_user_skb_frags(const struct iovec *iv, size_t count,
- struct skb_frag_struct *f)
+static int get_user_skb_frags(struct iovec *iv, size_t count,
+ struct skb_shared_info *sinfo)
{
- unsigned int i, j, num_pg = 0;
+ struct skb_frag_struct *f = sinfo->frags;
+ unsigned int i;
int err;
- struct page *pages[MAX_SKB_FRAGS];
- down_read(¤t->mm->mmap_sem);
+ f->page = NULL;
+
for (i = 0; i < count; i++) {
- int n, npages;
- unsigned long base, len;
- base = (unsigned long)iv[i].iov_base;
- len = (unsigned long)iv[i].iov_len;
+ unsigned int len = iv[i].iov_len;
- if (len == 0)
- continue;
+ while (len) {
+ void *virt;
+ unsigned int copy;
- /* How many pages will this take? */
- npages = 1 + (base + len - 1)/PAGE_SIZE - base/PAGE_SIZE;
- if (unlikely(num_pg + npages > MAX_SKB_FRAGS)) {
- err = -ENOSPC;
- goto fail;
- }
- n = get_user_pages(current, current->mm, base, npages,
- 0, 0, pages, NULL);
- if (unlikely(n < 0)) {
- err = n;
- goto fail;
- }
+ if (!f->page) {
+ f->page = alloc_page(GFP_KERNEL |
+ __GFP_HIGHMEM);
+ if (!f->page)
+ return -ENOMEM;
- /* Transfer pages to the frag array */
- for (j = 0; j < n; j++) {
- f[num_pg].page = pages[j];
- if (j == 0) {
- f[num_pg].page_offset = offset_in_page(base);
- f[num_pg].size = min(len, PAGE_SIZE -
- f[num_pg].page_offset);
- } else {
- f[num_pg].page_offset = 0;
- f[num_pg].size = min(len, PAGE_SIZE);
+ f->page_offset = 0;
+ f->size = 0;
+ sinfo->nr_frags++;
}
- len -= f[num_pg].size;
- base += f[num_pg].size;
- num_pg++;
- }
- if (unlikely(n != npages)) {
- err = -EFAULT;
- goto fail;
+ copy = PAGE_SIZE - f->size;
+ if (copy > len)
+ copy = len;
+
+ virt = kmap_atomic(f->page, KM_USER0);
+ err = memcpy_fromiovec(virt + f->size, iv, copy);
+ kunmap_atomic(virt, KM_USER0);
+
+ if (err)
+ return err;
+
+ f->size += copy;
+ if (f->size == PAGE_SIZE) {
+ if (sinfo->nr_frags >= MAX_SKB_FRAGS)
+ return -EMSGSIZE;
+ (++f)->page = NULL;
+ }
+ len -= copy;
}
}
- up_read(¤t->mm->mmap_sem);
- return num_pg;
-fail:
- for (i = 0; i < num_pg; i++)
- put_page(f[i].page);
- up_read(¤t->mm->mmap_sem);
- return err;
+ return 0;
}
@@ -360,13 +351,13 @@ static struct sk_buff *map_user_skb(const struct
virtio_net_hdr *gso,
goto fail;
}
- err = get_user_skb_frags(iv, count, sinfo->frags);
+ err = get_user_skb_frags(iv, count, sinfo);
if (err < 0)
goto fail;
- sinfo->nr_frags = err;
skb->len += len;
skb->data_len += len;
+ skb->truesize += len;
return skb;
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <[EMAIL PROTECTED]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/virtualization