brintos

brintos / linux-shallow public Read only

0
0
Text · 15.4 KiB · 608953d Raw
654 lines · c
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 this6 * 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 FITNESS11 * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE12 * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME13 * 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/debugfs.h>19#include <linux/kernel.h>20#include <linux/mutex.h>21#include <linux/rtnetlink.h>22#include <net/pkt_cls.h>23 24#include "netdevsim.h"25 26#define pr_vlog(env, fmt, ...)	\27	bpf_verifier_log_write(env, "[netdevsim] " fmt, ##__VA_ARGS__)28 29struct nsim_bpf_bound_prog {30	struct nsim_dev *nsim_dev;31	struct bpf_prog *prog;32	struct dentry *ddir;33	const char *state;34	bool is_loaded;35	struct list_head l;36};37 38#define NSIM_BPF_MAX_KEYS		239 40struct nsim_bpf_bound_map {41	struct netdevsim *ns;42	struct bpf_offloaded_map *map;43	struct mutex mutex;44	struct nsim_map_entry {45		void *key;46		void *value;47	} entry[NSIM_BPF_MAX_KEYS];48	struct list_head l;49};50 51static int nsim_bpf_string_show(struct seq_file *file, void *data)52{53	const char **str = file->private;54 55	if (*str)56		seq_printf(file, "%s\n", *str);57 58	return 0;59}60DEFINE_SHOW_ATTRIBUTE(nsim_bpf_string);61 62static int63nsim_bpf_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn)64{65	struct nsim_bpf_bound_prog *state;66	int ret = 0;67 68	state = env->prog->aux->offload->dev_priv;69	if (state->nsim_dev->bpf_bind_verifier_delay && !insn_idx)70		msleep(state->nsim_dev->bpf_bind_verifier_delay);71 72	if (insn_idx == env->prog->len - 1) {73		pr_vlog(env, "Hello from netdevsim!\n");74 75		if (!state->nsim_dev->bpf_bind_verifier_accept)76			ret = -EOPNOTSUPP;77	}78 79	return ret;80}81 82static int nsim_bpf_finalize(struct bpf_verifier_env *env)83{84	return 0;85}86 87static bool nsim_xdp_offload_active(struct netdevsim *ns)88{89	return ns->xdp_hw.prog;90}91 92static void nsim_prog_set_loaded(struct bpf_prog *prog, bool loaded)93{94	struct nsim_bpf_bound_prog *state;95 96	if (!prog || !bpf_prog_is_offloaded(prog->aux))97		return;98 99	state = prog->aux->offload->dev_priv;100	state->is_loaded = loaded;101}102 103static int104nsim_bpf_offload(struct netdevsim *ns, struct bpf_prog *prog, bool oldprog)105{106	nsim_prog_set_loaded(ns->bpf_offloaded, false);107 108	WARN(!!ns->bpf_offloaded != oldprog,109	     "bad offload state, expected offload %sto be active",110	     oldprog ? "" : "not ");111	ns->bpf_offloaded = prog;112	ns->bpf_offloaded_id = prog ? prog->aux->id : 0;113	nsim_prog_set_loaded(prog, true);114 115	return 0;116}117 118int nsim_bpf_setup_tc_block_cb(enum tc_setup_type type,119			       void *type_data, void *cb_priv)120{121	struct tc_cls_bpf_offload *cls_bpf = type_data;122	struct bpf_prog *prog = cls_bpf->prog;123	struct netdevsim *ns = cb_priv;124	struct bpf_prog *oldprog;125 126	if (type != TC_SETUP_CLSBPF) {127		NSIM_EA(cls_bpf->common.extack,128			"only offload of BPF classifiers supported");129		return -EOPNOTSUPP;130	}131 132	if (!tc_cls_can_offload_and_chain0(ns->netdev, &cls_bpf->common))133		return -EOPNOTSUPP;134 135	if (cls_bpf->common.protocol != htons(ETH_P_ALL)) {136		NSIM_EA(cls_bpf->common.extack,137			"only ETH_P_ALL supported as filter protocol");138		return -EOPNOTSUPP;139	}140 141	if (!ns->bpf_tc_accept) {142		NSIM_EA(cls_bpf->common.extack,143			"netdevsim configured to reject BPF TC offload");144		return -EOPNOTSUPP;145	}146	/* Note: progs without skip_sw will probably not be dev bound */147	if (prog && !prog->aux->offload && !ns->bpf_tc_non_bound_accept) {148		NSIM_EA(cls_bpf->common.extack,149			"netdevsim configured to reject unbound programs");150		return -EOPNOTSUPP;151	}152 153	if (cls_bpf->command != TC_CLSBPF_OFFLOAD)154		return -EOPNOTSUPP;155 156	oldprog = cls_bpf->oldprog;157 158	/* Don't remove if oldprog doesn't match driver's state */159	if (ns->bpf_offloaded != oldprog) {160		oldprog = NULL;161		if (!cls_bpf->prog)162			return 0;163		if (ns->bpf_offloaded) {164			NSIM_EA(cls_bpf->common.extack,165				"driver and netdev offload states mismatch");166			return -EBUSY;167		}168	}169 170	return nsim_bpf_offload(ns, cls_bpf->prog, oldprog);171}172 173int nsim_bpf_disable_tc(struct netdevsim *ns)174{175	if (ns->bpf_offloaded && !nsim_xdp_offload_active(ns))176		return -EBUSY;177	return 0;178}179 180static int nsim_xdp_offload_prog(struct netdevsim *ns, struct netdev_bpf *bpf)181{182	if (!nsim_xdp_offload_active(ns) && !bpf->prog)183		return 0;184	if (!nsim_xdp_offload_active(ns) && bpf->prog && ns->bpf_offloaded) {185		NSIM_EA(bpf->extack, "TC program is already loaded");186		return -EBUSY;187	}188 189	return nsim_bpf_offload(ns, bpf->prog, nsim_xdp_offload_active(ns));190}191 192static int193nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf,194		  struct xdp_attachment_info *xdp)195{196	int err;197 198	if (bpf->command == XDP_SETUP_PROG && !ns->bpf_xdpdrv_accept) {199		NSIM_EA(bpf->extack, "driver XDP disabled in DebugFS");200		return -EOPNOTSUPP;201	}202	if (bpf->command == XDP_SETUP_PROG_HW && !ns->bpf_xdpoffload_accept) {203		NSIM_EA(bpf->extack, "XDP offload disabled in DebugFS");204		return -EOPNOTSUPP;205	}206 207	if (bpf->command == XDP_SETUP_PROG_HW) {208		err = nsim_xdp_offload_prog(ns, bpf);209		if (err)210			return err;211	}212 213	xdp_attachment_setup(xdp, bpf);214 215	return 0;216}217 218static int nsim_bpf_create_prog(struct nsim_dev *nsim_dev,219				struct bpf_prog *prog)220{221	struct nsim_bpf_bound_prog *state;222	char name[16];223	int ret;224 225	state = kzalloc(sizeof(*state), GFP_KERNEL);226	if (!state)227		return -ENOMEM;228 229	state->nsim_dev = nsim_dev;230	state->prog = prog;231	state->state = "verify";232 233	/* Program id is not populated yet when we create the state. */234	sprintf(name, "%u", nsim_dev->prog_id_gen++);235	state->ddir = debugfs_create_dir(name, nsim_dev->ddir_bpf_bound_progs);236	if (IS_ERR(state->ddir)) {237		ret = PTR_ERR(state->ddir);238		kfree(state);239		return ret;240	}241 242	debugfs_create_u32("id", 0400, state->ddir, &prog->aux->id);243	debugfs_create_file("state", 0400, state->ddir,244			    &state->state, &nsim_bpf_string_fops);245	debugfs_create_bool("loaded", 0400, state->ddir, &state->is_loaded);246 247	list_add_tail(&state->l, &nsim_dev->bpf_bound_progs);248 249	prog->aux->offload->dev_priv = state;250 251	return 0;252}253 254static int nsim_bpf_verifier_prep(struct bpf_prog *prog)255{256	struct nsim_dev *nsim_dev =257			bpf_offload_dev_priv(prog->aux->offload->offdev);258 259	if (!nsim_dev->bpf_bind_accept)260		return -EOPNOTSUPP;261 262	return nsim_bpf_create_prog(nsim_dev, prog);263}264 265static int nsim_bpf_translate(struct bpf_prog *prog)266{267	struct nsim_bpf_bound_prog *state = prog->aux->offload->dev_priv;268 269	state->state = "xlated";270	return 0;271}272 273static void nsim_bpf_destroy_prog(struct bpf_prog *prog)274{275	struct nsim_bpf_bound_prog *state;276 277	state = prog->aux->offload->dev_priv;278	WARN(state->is_loaded,279	     "offload state destroyed while program still bound");280	debugfs_remove_recursive(state->ddir);281	list_del(&state->l);282	kfree(state);283}284 285static const struct bpf_prog_offload_ops nsim_bpf_dev_ops = {286	.insn_hook	= nsim_bpf_verify_insn,287	.finalize	= nsim_bpf_finalize,288	.prepare	= nsim_bpf_verifier_prep,289	.translate	= nsim_bpf_translate,290	.destroy	= nsim_bpf_destroy_prog,291};292 293static int nsim_setup_prog_checks(struct netdevsim *ns, struct netdev_bpf *bpf)294{295	if (bpf->prog && bpf->prog->aux->offload) {296		NSIM_EA(bpf->extack, "attempt to load offloaded prog to drv");297		return -EINVAL;298	}299	if (ns->netdev->mtu > NSIM_XDP_MAX_MTU) {300		NSIM_EA(bpf->extack, "MTU too large w/ XDP enabled");301		return -EINVAL;302	}303	return 0;304}305 306static int307nsim_setup_prog_hw_checks(struct netdevsim *ns, struct netdev_bpf *bpf)308{309	struct nsim_bpf_bound_prog *state;310 311	if (!bpf->prog)312		return 0;313 314	if (!bpf_prog_is_offloaded(bpf->prog->aux)) {315		NSIM_EA(bpf->extack, "xdpoffload of non-bound program");316		return -EINVAL;317	}318 319	state = bpf->prog->aux->offload->dev_priv;320	if (WARN_ON(strcmp(state->state, "xlated"))) {321		NSIM_EA(bpf->extack, "offloading program in bad state");322		return -EINVAL;323	}324	return 0;325}326 327static bool328nsim_map_key_match(struct bpf_map *map, struct nsim_map_entry *e, void *key)329{330	return e->key && !memcmp(key, e->key, map->key_size);331}332 333static int nsim_map_key_find(struct bpf_offloaded_map *offmap, void *key)334{335	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;336	unsigned int i;337 338	for (i = 0; i < ARRAY_SIZE(nmap->entry); i++)339		if (nsim_map_key_match(&offmap->map, &nmap->entry[i], key))340			return i;341 342	return -ENOENT;343}344 345static int346nsim_map_alloc_elem(struct bpf_offloaded_map *offmap, unsigned int idx)347{348	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;349 350	nmap->entry[idx].key = kmalloc(offmap->map.key_size,351				       GFP_KERNEL_ACCOUNT | __GFP_NOWARN);352	if (!nmap->entry[idx].key)353		return -ENOMEM;354	nmap->entry[idx].value = kmalloc(offmap->map.value_size,355					 GFP_KERNEL_ACCOUNT | __GFP_NOWARN);356	if (!nmap->entry[idx].value) {357		kfree(nmap->entry[idx].key);358		nmap->entry[idx].key = NULL;359		return -ENOMEM;360	}361 362	return 0;363}364 365static int366nsim_map_get_next_key(struct bpf_offloaded_map *offmap,367		      void *key, void *next_key)368{369	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;370	int idx = -ENOENT;371 372	mutex_lock(&nmap->mutex);373 374	if (key)375		idx = nsim_map_key_find(offmap, key);376	if (idx == -ENOENT)377		idx = 0;378	else379		idx++;380 381	for (; idx < ARRAY_SIZE(nmap->entry); idx++) {382		if (nmap->entry[idx].key) {383			memcpy(next_key, nmap->entry[idx].key,384			       offmap->map.key_size);385			break;386		}387	}388 389	mutex_unlock(&nmap->mutex);390 391	if (idx == ARRAY_SIZE(nmap->entry))392		return -ENOENT;393	return 0;394}395 396static int397nsim_map_lookup_elem(struct bpf_offloaded_map *offmap, void *key, void *value)398{399	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;400	int idx;401 402	mutex_lock(&nmap->mutex);403 404	idx = nsim_map_key_find(offmap, key);405	if (idx >= 0)406		memcpy(value, nmap->entry[idx].value, offmap->map.value_size);407 408	mutex_unlock(&nmap->mutex);409 410	return idx < 0 ? idx : 0;411}412 413static int414nsim_map_update_elem(struct bpf_offloaded_map *offmap,415		     void *key, void *value, u64 flags)416{417	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;418	int idx, err = 0;419 420	mutex_lock(&nmap->mutex);421 422	idx = nsim_map_key_find(offmap, key);423	if (idx < 0 && flags == BPF_EXIST) {424		err = idx;425		goto exit_unlock;426	}427	if (idx >= 0 && flags == BPF_NOEXIST) {428		err = -EEXIST;429		goto exit_unlock;430	}431 432	if (idx < 0) {433		for (idx = 0; idx < ARRAY_SIZE(nmap->entry); idx++)434			if (!nmap->entry[idx].key)435				break;436		if (idx == ARRAY_SIZE(nmap->entry)) {437			err = -E2BIG;438			goto exit_unlock;439		}440 441		err = nsim_map_alloc_elem(offmap, idx);442		if (err)443			goto exit_unlock;444	}445 446	memcpy(nmap->entry[idx].key, key, offmap->map.key_size);447	memcpy(nmap->entry[idx].value, value, offmap->map.value_size);448exit_unlock:449	mutex_unlock(&nmap->mutex);450 451	return err;452}453 454static int nsim_map_delete_elem(struct bpf_offloaded_map *offmap, void *key)455{456	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;457	int idx;458 459	if (offmap->map.map_type == BPF_MAP_TYPE_ARRAY)460		return -EINVAL;461 462	mutex_lock(&nmap->mutex);463 464	idx = nsim_map_key_find(offmap, key);465	if (idx >= 0) {466		kfree(nmap->entry[idx].key);467		kfree(nmap->entry[idx].value);468		memset(&nmap->entry[idx], 0, sizeof(nmap->entry[idx]));469	}470 471	mutex_unlock(&nmap->mutex);472 473	return idx < 0 ? idx : 0;474}475 476static const struct bpf_map_dev_ops nsim_bpf_map_ops = {477	.map_get_next_key	= nsim_map_get_next_key,478	.map_lookup_elem	= nsim_map_lookup_elem,479	.map_update_elem	= nsim_map_update_elem,480	.map_delete_elem	= nsim_map_delete_elem,481};482 483static int484nsim_bpf_map_alloc(struct netdevsim *ns, struct bpf_offloaded_map *offmap)485{486	struct nsim_bpf_bound_map *nmap;487	int i, err;488 489	if (WARN_ON(offmap->map.map_type != BPF_MAP_TYPE_ARRAY &&490		    offmap->map.map_type != BPF_MAP_TYPE_HASH))491		return -EINVAL;492	if (offmap->map.max_entries > NSIM_BPF_MAX_KEYS)493		return -ENOMEM;494	if (offmap->map.map_flags)495		return -EINVAL;496 497	nmap = kzalloc(sizeof(*nmap), GFP_KERNEL_ACCOUNT);498	if (!nmap)499		return -ENOMEM;500 501	offmap->dev_priv = nmap;502	nmap->ns = ns;503	nmap->map = offmap;504	mutex_init(&nmap->mutex);505 506	if (offmap->map.map_type == BPF_MAP_TYPE_ARRAY) {507		for (i = 0; i < ARRAY_SIZE(nmap->entry); i++) {508			u32 *key;509 510			err = nsim_map_alloc_elem(offmap, i);511			if (err)512				goto err_free;513			key = nmap->entry[i].key;514			*key = i;515			memset(nmap->entry[i].value, 0, offmap->map.value_size);516		}517	}518 519	offmap->dev_ops = &nsim_bpf_map_ops;520	list_add_tail(&nmap->l, &ns->nsim_dev->bpf_bound_maps);521 522	return 0;523 524err_free:525	while (--i >= 0) {526		kfree(nmap->entry[i].key);527		kfree(nmap->entry[i].value);528	}529	kfree(nmap);530	return err;531}532 533static void nsim_bpf_map_free(struct bpf_offloaded_map *offmap)534{535	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;536	unsigned int i;537 538	for (i = 0; i < ARRAY_SIZE(nmap->entry); i++) {539		kfree(nmap->entry[i].key);540		kfree(nmap->entry[i].value);541	}542	list_del_init(&nmap->l);543	mutex_destroy(&nmap->mutex);544	kfree(nmap);545}546 547int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf)548{549	struct netdevsim *ns = netdev_priv(dev);550	int err;551 552	ASSERT_RTNL();553 554	switch (bpf->command) {555	case XDP_SETUP_PROG:556		err = nsim_setup_prog_checks(ns, bpf);557		if (err)558			return err;559 560		return nsim_xdp_set_prog(ns, bpf, &ns->xdp);561	case XDP_SETUP_PROG_HW:562		err = nsim_setup_prog_hw_checks(ns, bpf);563		if (err)564			return err;565 566		return nsim_xdp_set_prog(ns, bpf, &ns->xdp_hw);567	case BPF_OFFLOAD_MAP_ALLOC:568		if (!ns->bpf_map_accept)569			return -EOPNOTSUPP;570 571		return nsim_bpf_map_alloc(ns, bpf->offmap);572	case BPF_OFFLOAD_MAP_FREE:573		nsim_bpf_map_free(bpf->offmap);574		return 0;575	default:576		return -EINVAL;577	}578}579 580int nsim_bpf_dev_init(struct nsim_dev *nsim_dev)581{582	int err;583 584	INIT_LIST_HEAD(&nsim_dev->bpf_bound_progs);585	INIT_LIST_HEAD(&nsim_dev->bpf_bound_maps);586 587	nsim_dev->ddir_bpf_bound_progs = debugfs_create_dir("bpf_bound_progs",588							    nsim_dev->ddir);589	if (IS_ERR(nsim_dev->ddir_bpf_bound_progs))590		return PTR_ERR(nsim_dev->ddir_bpf_bound_progs);591 592	nsim_dev->bpf_dev = bpf_offload_dev_create(&nsim_bpf_dev_ops, nsim_dev);593	err = PTR_ERR_OR_ZERO(nsim_dev->bpf_dev);594	if (err)595		return err;596 597	nsim_dev->bpf_bind_accept = true;598	debugfs_create_bool("bpf_bind_accept", 0600, nsim_dev->ddir,599			    &nsim_dev->bpf_bind_accept);600	debugfs_create_u32("bpf_bind_verifier_delay", 0600, nsim_dev->ddir,601			   &nsim_dev->bpf_bind_verifier_delay);602	nsim_dev->bpf_bind_verifier_accept = true;603	debugfs_create_bool("bpf_bind_verifier_accept", 0600, nsim_dev->ddir,604			    &nsim_dev->bpf_bind_verifier_accept);605	return 0;606}607 608void nsim_bpf_dev_exit(struct nsim_dev *nsim_dev)609{610	WARN_ON(!list_empty(&nsim_dev->bpf_bound_progs));611	WARN_ON(!list_empty(&nsim_dev->bpf_bound_maps));612	bpf_offload_dev_destroy(nsim_dev->bpf_dev);613}614 615int nsim_bpf_init(struct netdevsim *ns)616{617	struct dentry *ddir = ns->nsim_dev_port->ddir;618	int err;619 620	err = bpf_offload_dev_netdev_register(ns->nsim_dev->bpf_dev,621					      ns->netdev);622	if (err)623		return err;624 625	debugfs_create_u32("bpf_offloaded_id", 0400, ddir,626			   &ns->bpf_offloaded_id);627 628	ns->bpf_tc_accept = true;629	debugfs_create_bool("bpf_tc_accept", 0600, ddir,630			    &ns->bpf_tc_accept);631	debugfs_create_bool("bpf_tc_non_bound_accept", 0600, ddir,632			    &ns->bpf_tc_non_bound_accept);633	ns->bpf_xdpdrv_accept = true;634	debugfs_create_bool("bpf_xdpdrv_accept", 0600, ddir,635			    &ns->bpf_xdpdrv_accept);636	ns->bpf_xdpoffload_accept = true;637	debugfs_create_bool("bpf_xdpoffload_accept", 0600, ddir,638			    &ns->bpf_xdpoffload_accept);639 640	ns->bpf_map_accept = true;641	debugfs_create_bool("bpf_map_accept", 0600, ddir,642			    &ns->bpf_map_accept);643 644	return 0;645}646 647void nsim_bpf_uninit(struct netdevsim *ns)648{649	WARN_ON(ns->xdp.prog);650	WARN_ON(ns->xdp_hw.prog);651	WARN_ON(ns->bpf_offloaded);652	bpf_offload_dev_netdev_unregister(ns->nsim_dev->bpf_dev, ns->netdev);653}654