206 lines · 4.7 KB · C
RawDownload
/* * Copyright (C) 2017 Netronome Systems, Inc. * * This software is licensed under the GNU General License Version 2, * June 1991 as shown in the file COPYING in the top-level directory of this * source tree. * * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. */ #include <linux/bpf.h>#include <linux/bpf_verifier.h>#include <linux/bug.h>#include <linux/list.h>#include <linux/netdevice.h>#include <linux/printk.h>#include <linux/rtnetlink.h> /* protected by RTNL */static LIST_HEAD(bpf_prog_offload_devs); int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr){	struct net *net = current->nsproxy->net_ns;	struct bpf_dev_offload *offload; 	if (attr->prog_type != BPF_PROG_TYPE_SCHED_CLS &&	    attr->prog_type != BPF_PROG_TYPE_XDP)		return -EINVAL; 	if (attr->prog_flags)		return -EINVAL; 	offload = kzalloc(sizeof(*offload), GFP_USER);	if (!offload)		return -ENOMEM; 	offload->prog = prog;	init_waitqueue_head(&offload->verifier_done); 	rtnl_lock();	offload->netdev = __dev_get_by_index(net, attr->prog_ifindex);	if (!offload->netdev) {		rtnl_unlock();		kfree(offload);		return -EINVAL;	} 	prog->aux->offload = offload;	list_add_tail(&offload->offloads, &bpf_prog_offload_devs);	rtnl_unlock(); 	return 0;} static int __bpf_offload_ndo(struct bpf_prog *prog, enum bpf_netdev_command cmd,			     struct netdev_bpf *data){	struct net_device *netdev = prog->aux->offload->netdev; 	ASSERT_RTNL(); 	if (!netdev)		return -ENODEV;	if (!netdev->netdev_ops->ndo_bpf)		return -EOPNOTSUPP; 	data->command = cmd; 	return netdev->netdev_ops->ndo_bpf(netdev, data);} int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env){	struct netdev_bpf data = {};	int err; 	data.verifier.prog = env->prog; 	rtnl_lock();	err = __bpf_offload_ndo(env->prog, BPF_OFFLOAD_VERIFIER_PREP, &data);	if (err)		goto exit_unlock; 	env->dev_ops = data.verifier.ops; 	env->prog->aux->offload->dev_state = true;	env->prog->aux->offload->verifier_running = true;exit_unlock:	rtnl_unlock();	return err;} static void __bpf_prog_offload_destroy(struct bpf_prog *prog){	struct bpf_dev_offload *offload = prog->aux->offload;	struct netdev_bpf data = {}; 	/* Caution - if netdev is destroyed before the program, this function	 * will be called twice.	 */ 	data.offload.prog = prog; 	if (offload->verifier_running)		wait_event(offload->verifier_done, !offload->verifier_running); 	if (offload->dev_state)		WARN_ON(__bpf_offload_ndo(prog, BPF_OFFLOAD_DESTROY, &data)); 	offload->dev_state = false;	list_del_init(&offload->offloads);	offload->netdev = NULL;} void bpf_prog_offload_destroy(struct bpf_prog *prog){	struct bpf_dev_offload *offload = prog->aux->offload; 	offload->verifier_running = false;	wake_up(&offload->verifier_done); 	rtnl_lock();	__bpf_prog_offload_destroy(prog);	rtnl_unlock(); 	kfree(offload);} static int bpf_prog_offload_translate(struct bpf_prog *prog){	struct bpf_dev_offload *offload = prog->aux->offload;	struct netdev_bpf data = {};	int ret; 	data.offload.prog = prog; 	offload->verifier_running = false;	wake_up(&offload->verifier_done); 	rtnl_lock();	ret = __bpf_offload_ndo(prog, BPF_OFFLOAD_TRANSLATE, &data);	rtnl_unlock(); 	return ret;} static unsigned int bpf_prog_warn_on_exec(const void *ctx,					  const struct bpf_insn *insn){	WARN(1, "attempt to execute device eBPF program on the host!");	return 0;} int bpf_prog_offload_compile(struct bpf_prog *prog){	prog->bpf_func = bpf_prog_warn_on_exec; 	return bpf_prog_offload_translate(prog);} const struct bpf_prog_ops bpf_offload_prog_ops = {}; static int bpf_offload_notification(struct notifier_block *notifier,				    ulong event, void *ptr){	struct net_device *netdev = netdev_notifier_info_to_dev(ptr);	struct bpf_dev_offload *offload, *tmp; 	ASSERT_RTNL(); 	switch (event) {	case NETDEV_UNREGISTER:		/* ignore namespace changes */		if (netdev->reg_state != NETREG_UNREGISTERING)			break; 		list_for_each_entry_safe(offload, tmp, &bpf_prog_offload_devs,					 offloads) {			if (offload->netdev == netdev)				__bpf_prog_offload_destroy(offload->prog);		}		break;	default:		break;	}	return NOTIFY_OK;} static struct notifier_block bpf_offload_notifier = {	.notifier_call = bpf_offload_notification,}; static int __init bpf_offload_init(void){	register_netdevice_notifier(&bpf_offload_notifier);	return 0;} subsys_initcall(bpf_offload_init);