[3/6] [TUN]: Fix GSO mapping

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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(&current->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(&current->mm->mmap_sem);
-	return num_pg;
 
-fail:
-	for (i = 0; i < num_pg; i++)
-		put_page(f[i].page);
-	up_read(&current->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~} <herbert@xxxxxxxxxxxxxxxxxxx>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
_______________________________________________
Virtualization mailing list
Virtualization@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linux-foundation.org/mailman/listinfo/virtualization

[Index of Archives]     [KVM Development]     [Libvirt Development]     [Libvirt Users]     [CentOS Virtualization]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux