summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Wang <jasowang@redhat.com>2018-09-11 23:17:08 -0400
committerDavid S. Miller <davem@davemloft.net>2018-09-13 12:25:41 -0400
commit0efac27791ee068075d80f07c55a229b1335ce12 (patch)
tree91a1d78027c8581e429649970acc748951c36819
parent043d222f93ab8c76b56a3b315cd8692e35affb6c (diff)
tap: accept an array of XDP buffs through sendmsg()
This patch implement TUN_MSG_PTR msg_control type. This type allows the caller to pass an array of XDP buffs to tuntap through ptr field of the tun_msg_control. Tap will build skb through those XDP buffers. This will avoid lots of indirect calls thus improves the icache utilization and allows to do XDP batched flushing when doing XDP redirection. Signed-off-by: Jason Wang <jasowang@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/tap.c74
1 files changed, 72 insertions, 2 deletions
diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index 7996ed7cbf18..a4ab4a791fe7 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -1146,14 +1146,84 @@ static const struct file_operations tap_fops = {
1146#endif 1146#endif
1147}; 1147};
1148 1148
1149static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
1150{
1151 struct tun_xdp_hdr *hdr = xdp->data_hard_start;
1152 struct virtio_net_hdr *gso = &hdr->gso;
1153 int buflen = hdr->buflen;
1154 int vnet_hdr_len = 0;
1155 struct tap_dev *tap;
1156 struct sk_buff *skb;
1157 int err, depth;
1158
1159 if (q->flags & IFF_VNET_HDR)
1160 vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz);
1161
1162 skb = build_skb(xdp->data_hard_start, buflen);
1163 if (!skb) {
1164 err = -ENOMEM;
1165 goto err;
1166 }
1167
1168 skb_reserve(skb, xdp->data - xdp->data_hard_start);
1169 skb_put(skb, xdp->data_end - xdp->data);
1170
1171 skb_set_network_header(skb, ETH_HLEN);
1172 skb_reset_mac_header(skb);
1173 skb->protocol = eth_hdr(skb)->h_proto;
1174
1175 if (vnet_hdr_len) {
1176 err = virtio_net_hdr_to_skb(skb, gso, tap_is_little_endian(q));
1177 if (err)
1178 goto err_kfree;
1179 }
1180
1181 skb_probe_transport_header(skb, ETH_HLEN);
1182
1183 /* Move network header to the right position for VLAN tagged packets */
1184 if ((skb->protocol == htons(ETH_P_8021Q) ||
1185 skb->protocol == htons(ETH_P_8021AD)) &&
1186 __vlan_get_protocol(skb, skb->protocol, &depth) != 0)
1187 skb_set_network_header(skb, depth);
1188
1189 rcu_read_lock();
1190 tap = rcu_dereference(q->tap);
1191 if (tap) {
1192 skb->dev = tap->dev;
1193 dev_queue_xmit(skb);
1194 } else {
1195 kfree_skb(skb);
1196 }
1197 rcu_read_unlock();
1198
1199 return 0;
1200
1201err_kfree:
1202 kfree_skb(skb);
1203err:
1204 rcu_read_lock();
1205 tap = rcu_dereference(q->tap);
1206 if (tap && tap->count_tx_dropped)
1207 tap->count_tx_dropped(tap);
1208 rcu_read_unlock();
1209 return err;
1210}
1211
1149static int tap_sendmsg(struct socket *sock, struct msghdr *m, 1212static int tap_sendmsg(struct socket *sock, struct msghdr *m,
1150 size_t total_len) 1213 size_t total_len)
1151{ 1214{
1152 struct tap_queue *q = container_of(sock, struct tap_queue, sock); 1215 struct tap_queue *q = container_of(sock, struct tap_queue, sock);
1153 struct tun_msg_ctl *ctl = m->msg_control; 1216 struct tun_msg_ctl *ctl = m->msg_control;
1217 struct xdp_buff *xdp;
1218 int i;
1154 1219
1155 if (ctl && ctl->type != TUN_MSG_UBUF) 1220 if (ctl && (ctl->type == TUN_MSG_PTR)) {
1156 return -EINVAL; 1221 for (i = 0; i < ctl->num; i++) {
1222 xdp = &((struct xdp_buff *)ctl->ptr)[i];
1223 tap_get_user_xdp(q, xdp);
1224 }
1225 return 0;
1226 }
1157 1227
1158 return tap_get_user(q, ctl ? ctl->ptr : NULL, &m->msg_iter, 1228 return tap_get_user(q, ctl ? ctl->ptr : NULL, &m->msg_iter,
1159 m->msg_flags & MSG_DONTWAIT); 1229 m->msg_flags & MSG_DONTWAIT);