/* * 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 #include #include #include #include #include #include /* 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);