xref: /openbmc/linux/kernel/bpf/offload.c (revision cae1927c0b4a93ae15de824faca1f6f611a44fcd)
1 /*
2  * Copyright (C) 2017 Netronome Systems, Inc.
3  *
4  * This software is licensed under the GNU General License Version 2,
5  * June 1991 as shown in the file COPYING in the top-level directory of this
6  * source tree.
7  *
8  * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
9  * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
10  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
11  * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
12  * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
13  * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
14  */
15 
16 #include <linux/bpf.h>
17 #include <linux/bpf_verifier.h>
18 #include <linux/bug.h>
19 #include <linux/list.h>
20 #include <linux/netdevice.h>
21 #include <linux/printk.h>
22 #include <linux/rtnetlink.h>
23 #include <linux/rwsem.h>
24 
25 /* Protects bpf_prog_offload_devs and offload members of all progs.
26  * RTNL lock cannot be taken when holding this lock.
27  */
28 static DECLARE_RWSEM(bpf_devs_lock);
29 static LIST_HEAD(bpf_prog_offload_devs);
30 
31 int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr)
32 {
33 	struct bpf_dev_offload *offload;
34 
35 	if (attr->prog_type != BPF_PROG_TYPE_SCHED_CLS &&
36 	    attr->prog_type != BPF_PROG_TYPE_XDP)
37 		return -EINVAL;
38 
39 	if (attr->prog_flags)
40 		return -EINVAL;
41 
42 	offload = kzalloc(sizeof(*offload), GFP_USER);
43 	if (!offload)
44 		return -ENOMEM;
45 
46 	offload->prog = prog;
47 
48 	offload->netdev = dev_get_by_index(current->nsproxy->net_ns,
49 					   attr->prog_ifindex);
50 	if (!offload->netdev)
51 		goto err_free;
52 
53 	down_write(&bpf_devs_lock);
54 	if (offload->netdev->reg_state != NETREG_REGISTERED)
55 		goto err_unlock;
56 	prog->aux->offload = offload;
57 	list_add_tail(&offload->offloads, &bpf_prog_offload_devs);
58 	dev_put(offload->netdev);
59 	up_write(&bpf_devs_lock);
60 
61 	return 0;
62 err_unlock:
63 	up_write(&bpf_devs_lock);
64 	dev_put(offload->netdev);
65 err_free:
66 	kfree(offload);
67 	return -EINVAL;
68 }
69 
70 static int __bpf_offload_ndo(struct bpf_prog *prog, enum bpf_netdev_command cmd,
71 			     struct netdev_bpf *data)
72 {
73 	struct net_device *netdev = prog->aux->offload->netdev;
74 
75 	ASSERT_RTNL();
76 
77 	if (!netdev)
78 		return -ENODEV;
79 	if (!netdev->netdev_ops->ndo_bpf)
80 		return -EOPNOTSUPP;
81 
82 	data->command = cmd;
83 
84 	return netdev->netdev_ops->ndo_bpf(netdev, data);
85 }
86 
87 int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env)
88 {
89 	struct netdev_bpf data = {};
90 	int err;
91 
92 	data.verifier.prog = env->prog;
93 
94 	rtnl_lock();
95 	err = __bpf_offload_ndo(env->prog, BPF_OFFLOAD_VERIFIER_PREP, &data);
96 	if (err)
97 		goto exit_unlock;
98 
99 	env->prog->aux->offload->dev_ops = data.verifier.ops;
100 	env->prog->aux->offload->dev_state = true;
101 exit_unlock:
102 	rtnl_unlock();
103 	return err;
104 }
105 
106 int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env,
107 				 int insn_idx, int prev_insn_idx)
108 {
109 	struct bpf_dev_offload *offload;
110 	int ret = -ENODEV;
111 
112 	down_read(&bpf_devs_lock);
113 	offload = env->prog->aux->offload;
114 	if (offload->netdev)
115 		ret = offload->dev_ops->insn_hook(env, insn_idx, prev_insn_idx);
116 	up_read(&bpf_devs_lock);
117 
118 	return ret;
119 }
120 
121 static void __bpf_prog_offload_destroy(struct bpf_prog *prog)
122 {
123 	struct bpf_dev_offload *offload = prog->aux->offload;
124 	struct netdev_bpf data = {};
125 
126 	/* Caution - if netdev is destroyed before the program, this function
127 	 * will be called twice.
128 	 */
129 
130 	data.offload.prog = prog;
131 
132 	if (offload->dev_state)
133 		WARN_ON(__bpf_offload_ndo(prog, BPF_OFFLOAD_DESTROY, &data));
134 
135 	offload->dev_state = false;
136 	list_del_init(&offload->offloads);
137 	offload->netdev = NULL;
138 }
139 
140 void bpf_prog_offload_destroy(struct bpf_prog *prog)
141 {
142 	struct bpf_dev_offload *offload = prog->aux->offload;
143 
144 	rtnl_lock();
145 	down_write(&bpf_devs_lock);
146 	__bpf_prog_offload_destroy(prog);
147 	up_write(&bpf_devs_lock);
148 	rtnl_unlock();
149 
150 	kfree(offload);
151 }
152 
153 static int bpf_prog_offload_translate(struct bpf_prog *prog)
154 {
155 	struct netdev_bpf data = {};
156 	int ret;
157 
158 	data.offload.prog = prog;
159 
160 	rtnl_lock();
161 	ret = __bpf_offload_ndo(prog, BPF_OFFLOAD_TRANSLATE, &data);
162 	rtnl_unlock();
163 
164 	return ret;
165 }
166 
167 static unsigned int bpf_prog_warn_on_exec(const void *ctx,
168 					  const struct bpf_insn *insn)
169 {
170 	WARN(1, "attempt to execute device eBPF program on the host!");
171 	return 0;
172 }
173 
174 int bpf_prog_offload_compile(struct bpf_prog *prog)
175 {
176 	prog->bpf_func = bpf_prog_warn_on_exec;
177 
178 	return bpf_prog_offload_translate(prog);
179 }
180 
181 const struct bpf_prog_ops bpf_offload_prog_ops = {
182 };
183 
184 static int bpf_offload_notification(struct notifier_block *notifier,
185 				    ulong event, void *ptr)
186 {
187 	struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
188 	struct bpf_dev_offload *offload, *tmp;
189 
190 	ASSERT_RTNL();
191 
192 	switch (event) {
193 	case NETDEV_UNREGISTER:
194 		/* ignore namespace changes */
195 		if (netdev->reg_state != NETREG_UNREGISTERING)
196 			break;
197 
198 		down_write(&bpf_devs_lock);
199 		list_for_each_entry_safe(offload, tmp, &bpf_prog_offload_devs,
200 					 offloads) {
201 			if (offload->netdev == netdev)
202 				__bpf_prog_offload_destroy(offload->prog);
203 		}
204 		up_write(&bpf_devs_lock);
205 		break;
206 	default:
207 		break;
208 	}
209 	return NOTIFY_OK;
210 }
211 
212 static struct notifier_block bpf_offload_notifier = {
213 	.notifier_call = bpf_offload_notification,
214 };
215 
216 static int __init bpf_offload_init(void)
217 {
218 	register_netdevice_notifier(&bpf_offload_notifier);
219 	return 0;
220 }
221 
222 subsys_initcall(bpf_offload_init);
223