net: qmi_wwan: add a number of CMOTech devices
[firefly-linux-kernel-4.4.55.git] / drivers / net / macvtap.c
index 59e9605de316809a702951a5c6de3dbee3a43a69..9e56eb479a4fcb3b1d7d381b255381db0e07e0c4 100644 (file)
@@ -524,8 +524,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;
@@ -631,12 +633,35 @@ static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
        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 macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
                                const struct iovec *iv, unsigned long total_len,
                                size_t count, int noblock)
 {
+       int good_linear = SKB_MAX_HEAD(NET_IP_ALIGN);
        struct sk_buff *skb;
        struct macvlan_dev *vlan;
        unsigned long len = total_len;
@@ -645,6 +670,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
        int vnet_hdr_len = 0;
        int copylen = 0;
        bool zerocopy = false;
+       size_t linear;
 
        if (q->flags & IFF_VNET_HDR) {
                vnet_hdr_len = q->vnet_hdr_sz;
@@ -676,42 +702,40 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
        if (unlikely(count > UIO_MAXIOV))
                goto err;
 
-       if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY))
-               zerocopy = true;
+       if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY)) {
+               copylen = vnet_hdr.hdr_len ? vnet_hdr.hdr_len : GOODCOPY_LEN;
+               if (copylen > good_linear)
+                       copylen = good_linear;
+               linear = copylen;
+               if (iov_pages(iv, vnet_hdr_len + copylen, count)
+                   <= MAX_SKB_FRAGS)
+                       zerocopy = true;
+       }
 
-       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 < vnet_hdr_len)
-                               copylen = 0;
-                       else
-                               copylen -= vnet_hdr_len;
-               }
-               /* 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 buffer is mapped from userspace.
-                */
-               if (copylen < vnet_hdr.hdr_len)
-                       copylen = vnet_hdr.hdr_len;
-               if (!copylen)
-                       copylen = GOODCOPY_LEN;
-       } else
+       if (!zerocopy) {
                copylen = len;
+               if (vnet_hdr.hdr_len > good_linear)
+                       linear = good_linear;
+               else
+                       linear = vnet_hdr.hdr_len;
+       }
 
        skb = macvtap_alloc_skb(&q->sk, NET_IP_ALIGN, copylen,
-                               vnet_hdr.hdr_len, noblock, &err);
+                               linear, noblock, &err);
        if (!skb)
                goto err;
 
        if (zerocopy)
                err = zerocopy_sg_from_iovec(skb, iv, vnet_hdr_len, count);
-       else
+       else {
                err = skb_copy_datagram_from_iovec(skb, 0, iv, vnet_hdr_len,
                                                   len);
+               if (!err && m && m->msg_control) {
+                       struct ubuf_info *uarg = m->msg_control;
+                       uarg->callback(uarg, false);
+               }
+       }
+
        if (err)
                goto err_kfree;
 
@@ -773,11 +797,10 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q,
                                const struct sk_buff *skb,
                                const struct iovec *iv, int len)
 {
-       struct macvlan_dev *vlan;
        int ret;
        int vnet_hdr_len = 0;
        int vlan_offset = 0;
-       int copied;
+       int copied, total;
 
        if (q->flags & IFF_VNET_HDR) {
                struct virtio_net_hdr vnet_hdr;
@@ -792,7 +815,8 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q,
                if (memcpy_toiovecend(iv, (void *)&vnet_hdr, 0, sizeof(vnet_hdr)))
                        return -EFAULT;
        }
-       copied = vnet_hdr_len;
+       total = copied = vnet_hdr_len;
+       total += skb->len;
 
        if (!vlan_tx_tag_present(skb))
                len = min_t(int, skb->len, len);
@@ -807,6 +831,7 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q,
 
                vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto);
                len = min_t(int, skb->len + VLAN_HLEN, len);
+               total += VLAN_HLEN;
 
                copy = min_t(int, vlan_offset, len);
                ret = skb_copy_datagram_const_iovec(skb, 0, iv, copied, copy);
@@ -824,16 +849,9 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q,
        }
 
        ret = skb_copy_datagram_const_iovec(skb, vlan_offset, iv, copied, len);
-       copied += len;
 
 done:
-       rcu_read_lock_bh();
-       vlan = rcu_dereference_bh(q->vlan);
-       if (vlan)
-               macvlan_count_rx(vlan, copied - vnet_hdr_len, ret == 0, 0);
-       rcu_read_unlock_bh();
-
-       return ret ? ret : copied;
+       return ret ? ret : total;
 }
 
 static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb,
@@ -885,7 +903,9 @@ static ssize_t macvtap_aio_read(struct kiocb *iocb, const struct iovec *iv,
        }
 
        ret = macvtap_do_read(q, iocb, iv, len, file->f_flags & O_NONBLOCK);
-       ret = min_t(ssize_t, ret, len); /* XXX copied from tun.c. Why? */
+       ret = min_t(ssize_t, ret, len);
+       if (ret > 0)
+               iocb->ki_pos = ret;
 out:
        return ret;
 }