net: qmi_wwan: add Sierra Wireless MC73xx
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / net / tun.c
index bfa9bb48e42d5cfc06e299e64809e29ea3097b5b..582497103fe88ee4b659f646107437a66e784a2f 100644 (file)
@@ -1010,8 +1010,10 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from,
                        return -EMSGSIZE;
                num_pages = get_user_pages_fast(base, size, 0, &page[i]);
                if (num_pages != size) {
-                       for (i = 0; i < num_pages; i++)
-                               put_page(page[i]);
+                       int j;
+
+                       for (j = 0; j < num_pages; j++)
+                               put_page(page[i + j]);
                        return -EFAULT;
                }
                truesize = size * PAGE_SIZE;
@@ -1035,6 +1037,29 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from,
        return 0;
 }
 
+static unsigned long iov_pages(const struct iovec *iv, int offset,
+                              unsigned long nr_segs)
+{
+       unsigned long seg, base;
+       int pages = 0, len, size;
+
+       while (nr_segs && (offset >= iv->iov_len)) {
+               offset -= iv->iov_len;
+               ++iv;
+               --nr_segs;
+       }
+
+       for (seg = 0; seg < nr_segs; seg++) {
+               base = (unsigned long)iv[seg].iov_base + offset;
+               len = iv[seg].iov_len - offset;
+               size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT;
+               pages += size;
+               offset = 0;
+       }
+
+       return pages;
+}
+
 /* Get packet from user space buffer */
 static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
                            void *msg_control, const struct iovec *iv,
@@ -1042,8 +1067,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
 {
        struct tun_pi pi = { 0, cpu_to_be16(ETH_P_IP) };
        struct sk_buff *skb;
-       size_t len = total_len, align = NET_SKB_PAD;
+       size_t len = total_len, align = NET_SKB_PAD, linear;
        struct virtio_net_hdr gso = { 0 };
+       int good_linear;
        int offset = 0;
        int copylen;
        bool zerocopy = false;
@@ -1051,8 +1077,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
        u32 rxhash;
 
        if (!(tun->flags & TUN_NO_PI)) {
-               if ((len -= sizeof(pi)) > total_len)
+               if (len < sizeof(pi))
                        return -EINVAL;
+               len -= sizeof(pi);
 
                if (memcpy_fromiovecend((void *)&pi, iv, 0, sizeof(pi)))
                        return -EFAULT;
@@ -1060,8 +1087,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
        }
 
        if (tun->flags & TUN_VNET_HDR) {
-               if ((len -= tun->vnet_hdr_sz) > total_len)
+               if (len < tun->vnet_hdr_sz)
                        return -EINVAL;
+               len -= tun->vnet_hdr_sz;
 
                if (memcpy_fromiovecend((void *)&gso, iv, offset, sizeof(gso)))
                        return -EFAULT;
@@ -1082,34 +1110,30 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
                        return -EINVAL;
        }
 
-       if (msg_control)
-               zerocopy = true;
+       good_linear = SKB_MAX_HEAD(align);
 
-       if (zerocopy) {
-               /* Userspace may produce vectors with count greater than
-                * MAX_SKB_FRAGS, so we need to linearize parts of the skb
-                * to let the rest of data to be fit in the frags.
-                */
-               if (count > MAX_SKB_FRAGS) {
-                       copylen = iov_length(iv, count - MAX_SKB_FRAGS);
-                       if (copylen < offset)
-                               copylen = 0;
-                       else
-                               copylen -= offset;
-               } else
-                               copylen = 0;
-               /* There are 256 bytes to be copied in skb, so there is enough
-                * room for skb expand head in case it is used.
+       if (msg_control) {
+               /* There are 256 bytes to be copied in skb, so there is
+                * enough room for skb expand head in case it is used.
                 * The rest of the buffer is mapped from userspace.
                 */
-               if (copylen < gso.hdr_len)
-                       copylen = gso.hdr_len;
-               if (!copylen)
-                       copylen = GOODCOPY_LEN;
-       } else
+               copylen = gso.hdr_len ? gso.hdr_len : GOODCOPY_LEN;
+               if (copylen > good_linear)
+                       copylen = good_linear;
+               linear = copylen;
+               if (iov_pages(iv, offset + copylen, count) <= MAX_SKB_FRAGS)
+                       zerocopy = true;
+       }
+
+       if (!zerocopy) {
                copylen = len;
+               if (gso.hdr_len > good_linear)
+                       linear = good_linear;
+               else
+                       linear = gso.hdr_len;
+       }
 
-       skb = tun_alloc_skb(tfile, align, copylen, gso.hdr_len, noblock);
+       skb = tun_alloc_skb(tfile, align, copylen, linear, noblock);
        if (IS_ERR(skb)) {
                if (PTR_ERR(skb) != -EAGAIN)
                        tun->dev->stats.rx_dropped++;
@@ -1118,8 +1142,13 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
 
        if (zerocopy)
                err = zerocopy_sg_from_iovec(skb, iv, offset, count);
-       else
+       else {
                err = skb_copy_datagram_from_iovec(skb, 0, iv, offset, len);
+               if (!err && msg_control) {
+                       struct ubuf_info *uarg = msg_control;
+                       uarg->callback(uarg, false);
+               }
+       }
 
        if (err) {
                tun->dev->stats.rx_dropped++;
@@ -1383,6 +1412,8 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
        ret = tun_do_read(tun, tfile, iocb, iv, len,
                          file->f_flags & O_NONBLOCK);
        ret = min_t(ssize_t, ret, len);
+       if (ret > 0)
+               iocb->ki_pos = ret;
 out:
        tun_put(tun);
        return ret;
@@ -1672,11 +1703,11 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
                INIT_LIST_HEAD(&tun->disabled);
                err = tun_attach(tun, file);
                if (err < 0)
-                       goto err_free_dev;
+                       goto err_free_flow;
 
                err = register_netdevice(tun->dev);
                if (err < 0)
-                       goto err_free_dev;
+                       goto err_detach;
 
                if (device_create_file(&tun->dev->dev, &dev_attr_tun_flags) ||
                    device_create_file(&tun->dev->dev, &dev_attr_owner) ||
@@ -1720,7 +1751,12 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
        strcpy(ifr->ifr_name, tun->dev->name);
        return 0;
 
- err_free_dev:
+err_detach:
+       tun_detach_all(dev);
+err_free_flow:
+       tun_flow_uninit(tun);
+       security_tun_dev_free_security(tun->security);
+err_free_dev:
        free_netdev(dev);
        return err;
 }