xref: /openbmc/linux/net/bpf/test_run.c (revision 1cf1cae963c2e6032aebe1637e995bc2f5d330f4)
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