diff options
Diffstat (limited to 'drivers/net/tun.c')
| -rw-r--r-- | drivers/net/tun.c | 62 |
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 | ||
| 1038 | static 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 */ |
| 1039 | static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, | 1062 | static 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++; |
