summaryrefslogtreecommitdiffstats
path: root/drivers/net/tun.c
diff options
context:
space:
mode:
authorJason Wang <jasowang@redhat.com>2018-09-11 23:17:07 -0400
committerDavid S. Miller <davem@davemloft.net>2018-09-13 12:25:40 -0400
commit043d222f93ab8c76b56a3b315cd8692e35affb6c (patch)
treea801e9d961ce25b29b1a00603e8662735cefd525 /drivers/net/tun.c
parentfe8dd45bb7556246c6b76277b1ba4296c91c2505 (diff)
tuntap: 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. If an XDP program is attached, tuntap can run XDP program directly. If not, tuntap will build skb and do a fast receiving since part of the work has been done by vhost_net. 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>
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r--drivers/net/tun.c117
1 files changed, 114 insertions, 3 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 89779b58c7ca..2a2cd35853b7 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -2426,22 +2426,133 @@ static void tun_sock_write_space(struct sock *sk)
2426 kill_fasync(&tfile->fasync, SIGIO, POLL_OUT); 2426 kill_fasync(&tfile->fasync, SIGIO, POLL_OUT);
2427} 2427}
2428 2428
2429static int tun_xdp_one(struct tun_struct *tun,
2430 struct tun_file *tfile,
2431 struct xdp_buff *xdp, int *flush)
2432{
2433 struct tun_xdp_hdr *hdr = xdp->data_hard_start;
2434 struct virtio_net_hdr *gso = &hdr->gso;
2435 struct tun_pcpu_stats *stats;
2436 struct bpf_prog *xdp_prog;
2437 struct sk_buff *skb = NULL;
2438 u32 rxhash = 0, act;
2439 int buflen = hdr->buflen;
2440 int err = 0;
2441 bool skb_xdp = false;
2442
2443 xdp_prog = rcu_dereference(tun->xdp_prog);
2444 if (xdp_prog) {
2445 if (gso->gso_type) {
2446 skb_xdp = true;
2447 goto build;
2448 }
2449 xdp_set_data_meta_invalid(xdp);
2450 xdp->rxq = &tfile->xdp_rxq;
2451
2452 act = bpf_prog_run_xdp(xdp_prog, xdp);
2453 err = tun_xdp_act(tun, xdp_prog, xdp, act);
2454 if (err < 0) {
2455 put_page(virt_to_head_page(xdp->data));
2456 return err;
2457 }
2458
2459 switch (err) {
2460 case XDP_REDIRECT:
2461 *flush = true;
2462 /* fall through */
2463 case XDP_TX:
2464 return 0;
2465 case XDP_PASS:
2466 break;
2467 default:
2468 put_page(virt_to_head_page(xdp->data));
2469 return 0;
2470 }
2471 }
2472
2473build:
2474 skb = build_skb(xdp->data_hard_start, buflen);
2475 if (!skb) {
2476 err = -ENOMEM;
2477 goto out;
2478 }
2479
2480 skb_reserve(skb, xdp->data - xdp->data_hard_start);
2481 skb_put(skb, xdp->data_end - xdp->data);
2482
2483 if (virtio_net_hdr_to_skb(skb, gso, tun_is_little_endian(tun))) {
2484 this_cpu_inc(tun->pcpu_stats->rx_frame_errors);
2485 kfree_skb(skb);
2486 err = -EINVAL;
2487 goto out;
2488 }
2489
2490 skb->protocol = eth_type_trans(skb, tun->dev);
2491 skb_reset_network_header(skb);
2492 skb_probe_transport_header(skb, 0);
2493
2494 if (skb_xdp) {
2495 err = do_xdp_generic(xdp_prog, skb);
2496 if (err != XDP_PASS)
2497 goto out;
2498 }
2499
2500 if (!rcu_dereference(tun->steering_prog))
2501 rxhash = __skb_get_hash_symmetric(skb);
2502
2503 netif_receive_skb(skb);
2504
2505 stats = get_cpu_ptr(tun->pcpu_stats);
2506 u64_stats_update_begin(&stats->syncp);
2507 stats->rx_packets++;
2508 stats->rx_bytes += skb->len;
2509 u64_stats_update_end(&stats->syncp);
2510 put_cpu_ptr(stats);
2511
2512 if (rxhash)
2513 tun_flow_update(tun, rxhash, tfile);
2514
2515out:
2516 return err;
2517}
2518
2429static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len) 2519static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len)
2430{ 2520{
2431 int ret; 2521 int ret, i;
2432 struct tun_file *tfile = container_of(sock, struct tun_file, socket); 2522 struct tun_file *tfile = container_of(sock, struct tun_file, socket);
2433 struct tun_struct *tun = tun_get(tfile); 2523 struct tun_struct *tun = tun_get(tfile);
2434 struct tun_msg_ctl *ctl = m->msg_control; 2524 struct tun_msg_ctl *ctl = m->msg_control;
2525 struct xdp_buff *xdp;
2435 2526
2436 if (!tun) 2527 if (!tun)
2437 return -EBADFD; 2528 return -EBADFD;
2438 2529
2439 if (ctl && ctl->type != TUN_MSG_UBUF) 2530 if (ctl && (ctl->type == TUN_MSG_PTR)) {
2440 return -EINVAL; 2531 int n = ctl->num;
2532 int flush = 0;
2533
2534 local_bh_disable();
2535 rcu_read_lock();
2536
2537 for (i = 0; i < n; i++) {
2538 xdp = &((struct xdp_buff *)ctl->ptr)[i];
2539 tun_xdp_one(tun, tfile, xdp, &flush);
2540 }
2541
2542 if (flush)
2543 xdp_do_flush_map();
2544
2545 rcu_read_unlock();
2546 local_bh_enable();
2547
2548 ret = total_len;
2549 goto out;
2550 }
2441 2551
2442 ret = tun_get_user(tun, tfile, ctl ? ctl->ptr : NULL, &m->msg_iter, 2552 ret = tun_get_user(tun, tfile, ctl ? ctl->ptr : NULL, &m->msg_iter,
2443 m->msg_flags & MSG_DONTWAIT, 2553 m->msg_flags & MSG_DONTWAIT,
2444 m->msg_flags & MSG_MORE); 2554 m->msg_flags & MSG_MORE);
2555out:
2445 tun_put(tun); 2556 tun_put(tun);
2446 return ret; 2557 return ret;
2447} 2558}