|
@@ -698,6 +698,28 @@ 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,
|
|
@@ -744,31 +766,15 @@ 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 (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;
|
|
|
+ if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY)) {
|
|
|
+ copylen = vnet_hdr.hdr_len ? vnet_hdr.hdr_len : GOODCOPY_LEN;
|
|
|
linear = copylen;
|
|
|
- } else {
|
|
|
+ if (iov_pages(iv, vnet_hdr_len + copylen, count)
|
|
|
+ <= MAX_SKB_FRAGS)
|
|
|
+ zerocopy = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!zerocopy) {
|
|
|
copylen = len;
|
|
|
linear = vnet_hdr.hdr_len;
|
|
|
}
|
|
@@ -780,9 +786,15 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
|
|
|
|
|
|
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;
|
|
|
|