1*1cf1cae9SAlexei Starovoitov /* Copyright (c) 2017 Facebook 2*1cf1cae9SAlexei Starovoitov * 3*1cf1cae9SAlexei Starovoitov * This program is free software; you can redistribute it and/or 4*1cf1cae9SAlexei Starovoitov * modify it under the terms of version 2 of the GNU General Public 5*1cf1cae9SAlexei Starovoitov * License as published by the Free Software Foundation. 6*1cf1cae9SAlexei Starovoitov */ 7*1cf1cae9SAlexei Starovoitov #include <linux/bpf.h> 8*1cf1cae9SAlexei Starovoitov #include <linux/slab.h> 9*1cf1cae9SAlexei Starovoitov #include <linux/vmalloc.h> 10*1cf1cae9SAlexei Starovoitov #include <linux/etherdevice.h> 11*1cf1cae9SAlexei Starovoitov #include <linux/filter.h> 12*1cf1cae9SAlexei Starovoitov #include <linux/sched/signal.h> 13*1cf1cae9SAlexei Starovoitov 14*1cf1cae9SAlexei Starovoitov static __always_inline u32 bpf_test_run_one(struct bpf_prog *prog, void *ctx) 15*1cf1cae9SAlexei Starovoitov { 16*1cf1cae9SAlexei Starovoitov u32 ret; 17*1cf1cae9SAlexei Starovoitov 18*1cf1cae9SAlexei Starovoitov preempt_disable(); 19*1cf1cae9SAlexei Starovoitov rcu_read_lock(); 20*1cf1cae9SAlexei Starovoitov ret = BPF_PROG_RUN(prog, ctx); 21*1cf1cae9SAlexei Starovoitov rcu_read_unlock(); 22*1cf1cae9SAlexei Starovoitov preempt_enable(); 23*1cf1cae9SAlexei Starovoitov 24*1cf1cae9SAlexei Starovoitov return ret; 25*1cf1cae9SAlexei Starovoitov } 26*1cf1cae9SAlexei Starovoitov 27*1cf1cae9SAlexei Starovoitov static u32 bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, u32 *time) 28*1cf1cae9SAlexei Starovoitov { 29*1cf1cae9SAlexei Starovoitov u64 time_start, time_spent = 0; 30*1cf1cae9SAlexei Starovoitov u32 ret = 0, i; 31*1cf1cae9SAlexei Starovoitov 32*1cf1cae9SAlexei Starovoitov if (!repeat) 33*1cf1cae9SAlexei Starovoitov repeat = 1; 34*1cf1cae9SAlexei Starovoitov time_start = ktime_get_ns(); 35*1cf1cae9SAlexei Starovoitov for (i = 0; i < repeat; i++) { 36*1cf1cae9SAlexei Starovoitov ret = bpf_test_run_one(prog, ctx); 37*1cf1cae9SAlexei Starovoitov if (need_resched()) { 38*1cf1cae9SAlexei Starovoitov if (signal_pending(current)) 39*1cf1cae9SAlexei Starovoitov break; 40*1cf1cae9SAlexei Starovoitov time_spent += ktime_get_ns() - time_start; 41*1cf1cae9SAlexei Starovoitov cond_resched(); 42*1cf1cae9SAlexei Starovoitov time_start = ktime_get_ns(); 43*1cf1cae9SAlexei Starovoitov } 44*1cf1cae9SAlexei Starovoitov } 45*1cf1cae9SAlexei Starovoitov time_spent += ktime_get_ns() - time_start; 46*1cf1cae9SAlexei Starovoitov do_div(time_spent, repeat); 47*1cf1cae9SAlexei Starovoitov *time = time_spent > U32_MAX ? U32_MAX : (u32)time_spent; 48*1cf1cae9SAlexei Starovoitov 49*1cf1cae9SAlexei Starovoitov return ret; 50*1cf1cae9SAlexei Starovoitov } 51*1cf1cae9SAlexei Starovoitov 52*1cf1cae9SAlexei Starovoitov static int bpf_test_finish(union bpf_attr __user *uattr, const void *data, 53*1cf1cae9SAlexei Starovoitov u32 size, u32 retval, u32 duration) 54*1cf1cae9SAlexei Starovoitov { 55*1cf1cae9SAlexei Starovoitov void __user *data_out = u64_to_user_ptr(uattr->test.data_out); 56*1cf1cae9SAlexei Starovoitov int err = -EFAULT; 57*1cf1cae9SAlexei Starovoitov 58*1cf1cae9SAlexei Starovoitov if (data_out && copy_to_user(data_out, data, size)) 59*1cf1cae9SAlexei Starovoitov goto out; 60*1cf1cae9SAlexei Starovoitov if (copy_to_user(&uattr->test.data_size_out, &size, sizeof(size))) 61*1cf1cae9SAlexei Starovoitov goto out; 62*1cf1cae9SAlexei Starovoitov if (copy_to_user(&uattr->test.retval, &retval, sizeof(retval))) 63*1cf1cae9SAlexei Starovoitov goto out; 64*1cf1cae9SAlexei Starovoitov if (copy_to_user(&uattr->test.duration, &duration, sizeof(duration))) 65*1cf1cae9SAlexei Starovoitov goto out; 66*1cf1cae9SAlexei Starovoitov err = 0; 67*1cf1cae9SAlexei Starovoitov out: 68*1cf1cae9SAlexei Starovoitov return err; 69*1cf1cae9SAlexei Starovoitov } 70*1cf1cae9SAlexei Starovoitov 71*1cf1cae9SAlexei Starovoitov static void *bpf_test_init(const union bpf_attr *kattr, u32 size, 72*1cf1cae9SAlexei Starovoitov u32 headroom, u32 tailroom) 73*1cf1cae9SAlexei Starovoitov { 74*1cf1cae9SAlexei Starovoitov void __user *data_in = u64_to_user_ptr(kattr->test.data_in); 75*1cf1cae9SAlexei Starovoitov void *data; 76*1cf1cae9SAlexei Starovoitov 77*1cf1cae9SAlexei Starovoitov if (size < ETH_HLEN || size > PAGE_SIZE - headroom - tailroom) 78*1cf1cae9SAlexei Starovoitov return ERR_PTR(-EINVAL); 79*1cf1cae9SAlexei Starovoitov 80*1cf1cae9SAlexei Starovoitov data = kzalloc(size + headroom + tailroom, GFP_USER); 81*1cf1cae9SAlexei Starovoitov if (!data) 82*1cf1cae9SAlexei Starovoitov return ERR_PTR(-ENOMEM); 83*1cf1cae9SAlexei Starovoitov 84*1cf1cae9SAlexei Starovoitov if (copy_from_user(data + headroom, data_in, size)) { 85*1cf1cae9SAlexei Starovoitov kfree(data); 86*1cf1cae9SAlexei Starovoitov return ERR_PTR(-EFAULT); 87*1cf1cae9SAlexei Starovoitov } 88*1cf1cae9SAlexei Starovoitov return data; 89*1cf1cae9SAlexei Starovoitov } 90*1cf1cae9SAlexei Starovoitov 91*1cf1cae9SAlexei Starovoitov int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, 92*1cf1cae9SAlexei Starovoitov union bpf_attr __user *uattr) 93*1cf1cae9SAlexei Starovoitov { 94*1cf1cae9SAlexei Starovoitov bool is_l2 = false, is_direct_pkt_access = false; 95*1cf1cae9SAlexei Starovoitov u32 size = kattr->test.data_size_in; 96*1cf1cae9SAlexei Starovoitov u32 repeat = kattr->test.repeat; 97*1cf1cae9SAlexei Starovoitov u32 retval, duration; 98*1cf1cae9SAlexei Starovoitov struct sk_buff *skb; 99*1cf1cae9SAlexei Starovoitov void *data; 100*1cf1cae9SAlexei Starovoitov int ret; 101*1cf1cae9SAlexei Starovoitov 102*1cf1cae9SAlexei Starovoitov data = bpf_test_init(kattr, size, NET_SKB_PAD, 103*1cf1cae9SAlexei Starovoitov SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); 104*1cf1cae9SAlexei Starovoitov if (IS_ERR(data)) 105*1cf1cae9SAlexei Starovoitov return PTR_ERR(data); 106*1cf1cae9SAlexei Starovoitov 107*1cf1cae9SAlexei Starovoitov switch (prog->type) { 108*1cf1cae9SAlexei Starovoitov case BPF_PROG_TYPE_SCHED_CLS: 109*1cf1cae9SAlexei Starovoitov case BPF_PROG_TYPE_SCHED_ACT: 110*1cf1cae9SAlexei Starovoitov is_l2 = true; 111*1cf1cae9SAlexei Starovoitov /* fall through */ 112*1cf1cae9SAlexei Starovoitov case BPF_PROG_TYPE_LWT_IN: 113*1cf1cae9SAlexei Starovoitov case BPF_PROG_TYPE_LWT_OUT: 114*1cf1cae9SAlexei Starovoitov case BPF_PROG_TYPE_LWT_XMIT: 115*1cf1cae9SAlexei Starovoitov is_direct_pkt_access = true; 116*1cf1cae9SAlexei Starovoitov break; 117*1cf1cae9SAlexei Starovoitov default: 118*1cf1cae9SAlexei Starovoitov break; 119*1cf1cae9SAlexei Starovoitov } 120*1cf1cae9SAlexei Starovoitov 121*1cf1cae9SAlexei Starovoitov skb = build_skb(data, 0); 122*1cf1cae9SAlexei Starovoitov if (!skb) { 123*1cf1cae9SAlexei Starovoitov kfree(data); 124*1cf1cae9SAlexei Starovoitov return -ENOMEM; 125*1cf1cae9SAlexei Starovoitov } 126*1cf1cae9SAlexei Starovoitov 127*1cf1cae9SAlexei Starovoitov skb_reserve(skb, NET_SKB_PAD); 128*1cf1cae9SAlexei Starovoitov __skb_put(skb, size); 129*1cf1cae9SAlexei Starovoitov skb->protocol = eth_type_trans(skb, current->nsproxy->net_ns->loopback_dev); 130*1cf1cae9SAlexei Starovoitov skb_reset_network_header(skb); 131*1cf1cae9SAlexei Starovoitov 132*1cf1cae9SAlexei Starovoitov if (is_l2) 133*1cf1cae9SAlexei Starovoitov __skb_push(skb, ETH_HLEN); 134*1cf1cae9SAlexei Starovoitov if (is_direct_pkt_access) 135*1cf1cae9SAlexei Starovoitov bpf_compute_data_end(skb); 136*1cf1cae9SAlexei Starovoitov retval = bpf_test_run(prog, skb, repeat, &duration); 137*1cf1cae9SAlexei Starovoitov if (!is_l2) 138*1cf1cae9SAlexei Starovoitov __skb_push(skb, ETH_HLEN); 139*1cf1cae9SAlexei Starovoitov size = skb->len; 140*1cf1cae9SAlexei Starovoitov /* bpf program can never convert linear skb to non-linear */ 141*1cf1cae9SAlexei Starovoitov if (WARN_ON_ONCE(skb_is_nonlinear(skb))) 142*1cf1cae9SAlexei Starovoitov size = skb_headlen(skb); 143*1cf1cae9SAlexei Starovoitov ret = bpf_test_finish(uattr, skb->data, size, retval, duration); 144*1cf1cae9SAlexei Starovoitov kfree_skb(skb); 145*1cf1cae9SAlexei Starovoitov return ret; 146*1cf1cae9SAlexei Starovoitov } 147*1cf1cae9SAlexei Starovoitov 148*1cf1cae9SAlexei Starovoitov int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, 149*1cf1cae9SAlexei Starovoitov union bpf_attr __user *uattr) 150*1cf1cae9SAlexei Starovoitov { 151*1cf1cae9SAlexei Starovoitov u32 size = kattr->test.data_size_in; 152*1cf1cae9SAlexei Starovoitov u32 repeat = kattr->test.repeat; 153*1cf1cae9SAlexei Starovoitov struct xdp_buff xdp = {}; 154*1cf1cae9SAlexei Starovoitov u32 retval, duration; 155*1cf1cae9SAlexei Starovoitov void *data; 156*1cf1cae9SAlexei Starovoitov int ret; 157*1cf1cae9SAlexei Starovoitov 158*1cf1cae9SAlexei Starovoitov data = bpf_test_init(kattr, size, XDP_PACKET_HEADROOM, 0); 159*1cf1cae9SAlexei Starovoitov if (IS_ERR(data)) 160*1cf1cae9SAlexei Starovoitov return PTR_ERR(data); 161*1cf1cae9SAlexei Starovoitov 162*1cf1cae9SAlexei Starovoitov xdp.data_hard_start = data; 163*1cf1cae9SAlexei Starovoitov xdp.data = data + XDP_PACKET_HEADROOM; 164*1cf1cae9SAlexei Starovoitov xdp.data_end = xdp.data + size; 165*1cf1cae9SAlexei Starovoitov 166*1cf1cae9SAlexei Starovoitov retval = bpf_test_run(prog, &xdp, repeat, &duration); 167*1cf1cae9SAlexei Starovoitov if (xdp.data != data + XDP_PACKET_HEADROOM) 168*1cf1cae9SAlexei Starovoitov size = xdp.data_end - xdp.data; 169*1cf1cae9SAlexei Starovoitov ret = bpf_test_finish(uattr, xdp.data, size, retval, duration); 170*1cf1cae9SAlexei Starovoitov kfree(data); 171*1cf1cae9SAlexei Starovoitov return ret; 172*1cf1cae9SAlexei Starovoitov } 173