]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/net/macvtap.c
net: move zerocopy_sg_from_iovec() to net/core/datagram.c
[karo-tx-linux.git] / drivers / net / macvtap.c
index f2c4a3b218fc133b97e0697e1202e7a17017f7f3..182364abfa3567901b5b088664b5649163b7c4de 100644 (file)
@@ -536,86 +536,6 @@ static inline struct sk_buff *macvtap_alloc_skb(struct sock *sk, size_t prepad,
        return skb;
 }
 
-/* set skb frags from iovec, this can move to core network code for reuse */
-static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from,
-                                 int offset, size_t count)
-{
-       int len = iov_length(from, count) - offset;
-       int copy = skb_headlen(skb);
-       int size, offset1 = 0;
-       int i = 0;
-
-       /* Skip over from offset */
-       while (count && (offset >= from->iov_len)) {
-               offset -= from->iov_len;
-               ++from;
-               --count;
-       }
-
-       /* copy up to skb headlen */
-       while (count && (copy > 0)) {
-               size = min_t(unsigned int, copy, from->iov_len - offset);
-               if (copy_from_user(skb->data + offset1, from->iov_base + offset,
-                                  size))
-                       return -EFAULT;
-               if (copy > size) {
-                       ++from;
-                       --count;
-                       offset = 0;
-               } else
-                       offset += size;
-               copy -= size;
-               offset1 += size;
-       }
-
-       if (len == offset1)
-               return 0;
-
-       while (count--) {
-               struct page *page[MAX_SKB_FRAGS];
-               int num_pages;
-               unsigned long base;
-               unsigned long truesize;
-
-               len = from->iov_len - offset;
-               if (!len) {
-                       offset = 0;
-                       ++from;
-                       continue;
-               }
-               base = (unsigned long)from->iov_base + offset;
-               size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT;
-               if (i + size > MAX_SKB_FRAGS)
-                       return -EMSGSIZE;
-               num_pages = get_user_pages_fast(base, size, 0, &page[i]);
-               if (num_pages != size) {
-                       int j;
-
-                       for (j = 0; j < num_pages; j++)
-                               put_page(page[i + j]);
-                       return -EFAULT;
-               }
-               truesize = size * PAGE_SIZE;
-               skb->data_len += len;
-               skb->len += len;
-               skb->truesize += truesize;
-               atomic_add(truesize, &skb->sk->sk_wmem_alloc);
-               while (len) {
-                       int off = base & ~PAGE_MASK;
-                       int size = min_t(int, len, PAGE_SIZE - off);
-                       __skb_fill_page_desc(skb, i, page[i], off, size);
-                       skb_shinfo(skb)->nr_frags++;
-                       /* increase sk_wmem_alloc */
-                       base += size;
-                       len -= size;
-                       i++;
-               }
-               offset = 0;
-               ++from;
-       }
-       return 0;
-}
-
 /*
  * macvtap_skb_from_vnet_hdr and macvtap_skb_to_vnet_hdr should
  * be shared with the tun/tap driver.
@@ -698,7 +618,6 @@ static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
        return 0;
 }
 
-
 /* 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,
@@ -712,6 +631,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;
@@ -743,42 +663,35 @@ 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;
+               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;
+               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;
 
@@ -869,7 +782,7 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q,
                        __be16 h_vlan_proto;
                        __be16 h_vlan_TCI;
                } veth;
-               veth.h_vlan_proto = htons(ETH_P_8021Q);
+               veth.h_vlan_proto = skb->vlan_proto;
                veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb));
 
                vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto);
@@ -1103,6 +1016,7 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
                rtnl_lock();
                ret = macvtap_ioctl_set_queue(file, u);
                rtnl_unlock();
+               return ret;
 
        case TUNGETFEATURES:
                if (put_user(IFF_TAP | IFF_NO_PI | IFF_VNET_HDR |