aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/tun.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r--drivers/net/tun.c62
1 files changed, 38 insertions, 24 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 5cdcf92eb310..db690a372260 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1035,6 +1035,29 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from,
1035 return 0; 1035 return 0;
1036} 1036}
1037 1037
1038static unsigned long iov_pages(const struct iovec *iv, int offset,
1039 unsigned long nr_segs)
1040{
1041 unsigned long seg, base;
1042 int pages = 0, len, size;
1043
1044 while (nr_segs && (offset >= iv->iov_len)) {
1045 offset -= iv->iov_len;
1046 ++iv;
1047 --nr_segs;
1048 }
1049
1050 for (seg = 0; seg < nr_segs; seg++) {
1051 base = (unsigned long)iv[seg].iov_base + offset;
1052 len = iv[seg].iov_len - offset;
1053 size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT;
1054 pages += size;
1055 offset = 0;
1056 }
1057
1058 return pages;
1059}
1060
1038/* Get packet from user space buffer */ 1061/* Get packet from user space buffer */
1039static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, 1062static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
1040 void *msg_control, const struct iovec *iv, 1063 void *msg_control, const struct iovec *iv,
@@ -1082,32 +1105,18 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
1082 return -EINVAL; 1105 return -EINVAL;
1083 } 1106 }
1084 1107
1085 if (msg_control) 1108 if (msg_control) {
1086 zerocopy = true; 1109 /* There are 256 bytes to be copied in skb, so there is
1087 1110 * enough room for skb expand head in case it is used.
1088 if (zerocopy) {
1089 /* Userspace may produce vectors with count greater than
1090 * MAX_SKB_FRAGS, so we need to linearize parts of the skb
1091 * to let the rest of data to be fit in the frags.
1092 */
1093 if (count > MAX_SKB_FRAGS) {
1094 copylen = iov_length(iv, count - MAX_SKB_FRAGS);
1095 if (copylen < offset)
1096 copylen = 0;
1097 else
1098 copylen -= offset;
1099 } else
1100 copylen = 0;
1101 /* There are 256 bytes to be copied in skb, so there is enough
1102 * room for skb expand head in case it is used.
1103 * The rest of the buffer is mapped from userspace. 1111 * The rest of the buffer is mapped from userspace.
1104 */ 1112 */
1105 if (copylen < gso.hdr_len) 1113 copylen = gso.hdr_len ? gso.hdr_len : GOODCOPY_LEN;
1106 copylen = gso.hdr_len;
1107 if (!copylen)
1108 copylen = GOODCOPY_LEN;
1109 linear = copylen; 1114 linear = copylen;
1110 } else { 1115 if (iov_pages(iv, offset + copylen, count) <= MAX_SKB_FRAGS)
1116 zerocopy = true;
1117 }
1118
1119 if (!zerocopy) {
1111 copylen = len; 1120 copylen = len;
1112 linear = gso.hdr_len; 1121 linear = gso.hdr_len;
1113 } 1122 }
@@ -1121,8 +1130,13 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
1121 1130
1122 if (zerocopy) 1131 if (zerocopy)
1123 err = zerocopy_sg_from_iovec(skb, iv, offset, count); 1132 err = zerocopy_sg_from_iovec(skb, iv, offset, count);
1124 else 1133 else {
1125 err = skb_copy_datagram_from_iovec(skb, 0, iv, offset, len); 1134 err = skb_copy_datagram_from_iovec(skb, 0, iv, offset, len);
1135 if (!err && msg_control) {
1136 struct ubuf_info *uarg = msg_control;
1137 uarg->callback(uarg, false);
1138 }
1139 }
1126 1140
1127 if (err) { 1141 if (err) {
1128 tun->dev->stats.rx_dropped++; 1142 tun->dev->stats.rx_dropped++;