diff options
Diffstat (limited to 'kernel/bpf')
| -rw-r--r-- | kernel/bpf/Makefile | 1 | ||||
| -rw-r--r-- | kernel/bpf/core.c | 10 | ||||
| -rw-r--r-- | kernel/bpf/offload.c | 182 | ||||
| -rw-r--r-- | kernel/bpf/syscall.c | 17 | ||||
| -rw-r--r-- | kernel/bpf/verifier.c | 15 |
5 files changed, 217 insertions, 8 deletions
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 16e95c8e749e..e691da0b3bab 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile | |||
| @@ -7,6 +7,7 @@ obj-$(CONFIG_BPF_SYSCALL) += disasm.o | |||
| 7 | ifeq ($(CONFIG_NET),y) | 7 | ifeq ($(CONFIG_NET),y) |
| 8 | obj-$(CONFIG_BPF_SYSCALL) += devmap.o | 8 | obj-$(CONFIG_BPF_SYSCALL) += devmap.o |
| 9 | obj-$(CONFIG_BPF_SYSCALL) += cpumap.o | 9 | obj-$(CONFIG_BPF_SYSCALL) += cpumap.o |
| 10 | obj-$(CONFIG_BPF_SYSCALL) += offload.o | ||
| 10 | ifeq ($(CONFIG_STREAM_PARSER),y) | 11 | ifeq ($(CONFIG_STREAM_PARSER),y) |
| 11 | obj-$(CONFIG_BPF_SYSCALL) += sockmap.o | 12 | obj-$(CONFIG_BPF_SYSCALL) += sockmap.o |
| 12 | endif | 13 | endif |
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 7fe448799d76..8a6c37762330 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c | |||
| @@ -1380,7 +1380,13 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err) | |||
| 1380 | * valid program, which in this case would simply not | 1380 | * valid program, which in this case would simply not |
| 1381 | * be JITed, but falls back to the interpreter. | 1381 | * be JITed, but falls back to the interpreter. |
| 1382 | */ | 1382 | */ |
| 1383 | fp = bpf_int_jit_compile(fp); | 1383 | if (!bpf_prog_is_dev_bound(fp->aux)) { |
| 1384 | fp = bpf_int_jit_compile(fp); | ||
| 1385 | } else { | ||
| 1386 | *err = bpf_prog_offload_compile(fp); | ||
| 1387 | if (*err) | ||
| 1388 | return fp; | ||
| 1389 | } | ||
| 1384 | bpf_prog_lock_ro(fp); | 1390 | bpf_prog_lock_ro(fp); |
| 1385 | 1391 | ||
| 1386 | /* The tail call compatibility check can only be done at | 1392 | /* The tail call compatibility check can only be done at |
| @@ -1549,6 +1555,8 @@ static void bpf_prog_free_deferred(struct work_struct *work) | |||
| 1549 | struct bpf_prog_aux *aux; | 1555 | struct bpf_prog_aux *aux; |
| 1550 | 1556 | ||
| 1551 | aux = container_of(work, struct bpf_prog_aux, work); | 1557 | aux = container_of(work, struct bpf_prog_aux, work); |
| 1558 | if (bpf_prog_is_dev_bound(aux)) | ||
| 1559 | bpf_prog_offload_destroy(aux->prog); | ||
| 1552 | bpf_jit_free(aux->prog); | 1560 | bpf_jit_free(aux->prog); |
| 1553 | } | 1561 | } |
| 1554 | 1562 | ||
diff --git a/kernel/bpf/offload.c b/kernel/bpf/offload.c new file mode 100644 index 000000000000..5553e0e2f8b1 --- /dev/null +++ b/kernel/bpf/offload.c | |||
| @@ -0,0 +1,182 @@ | |||
| 1 | #include <linux/bpf.h> | ||
| 2 | #include <linux/bpf_verifier.h> | ||
| 3 | #include <linux/bug.h> | ||
| 4 | #include <linux/list.h> | ||
| 5 | #include <linux/netdevice.h> | ||
| 6 | #include <linux/printk.h> | ||
| 7 | #include <linux/rtnetlink.h> | ||
| 8 | |||
| 9 | /* protected by RTNL */ | ||
| 10 | static LIST_HEAD(bpf_prog_offload_devs); | ||
| 11 | |||
| 12 | int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr) | ||
| 13 | { | ||
| 14 | struct net *net = current->nsproxy->net_ns; | ||
| 15 | struct bpf_dev_offload *offload; | ||
| 16 | |||
| 17 | if (!capable(CAP_SYS_ADMIN)) | ||
| 18 | return -EPERM; | ||
| 19 | |||
| 20 | if (attr->prog_flags) | ||
| 21 | return -EINVAL; | ||
| 22 | |||
| 23 | offload = kzalloc(sizeof(*offload), GFP_USER); | ||
| 24 | if (!offload) | ||
| 25 | return -ENOMEM; | ||
| 26 | |||
| 27 | offload->prog = prog; | ||
| 28 | init_waitqueue_head(&offload->verifier_done); | ||
| 29 | |||
| 30 | rtnl_lock(); | ||
| 31 | offload->netdev = __dev_get_by_index(net, attr->prog_target_ifindex); | ||
| 32 | if (!offload->netdev) { | ||
| 33 | rtnl_unlock(); | ||
| 34 | kfree(offload); | ||
| 35 | return -EINVAL; | ||
| 36 | } | ||
| 37 | |||
| 38 | prog->aux->offload = offload; | ||
| 39 | list_add_tail(&offload->offloads, &bpf_prog_offload_devs); | ||
| 40 | rtnl_unlock(); | ||
| 41 | |||
| 42 | return 0; | ||
| 43 | } | ||
| 44 | |||
| 45 | static int __bpf_offload_ndo(struct bpf_prog *prog, enum bpf_netdev_command cmd, | ||
| 46 | struct netdev_bpf *data) | ||
| 47 | { | ||
| 48 | struct net_device *netdev = prog->aux->offload->netdev; | ||
| 49 | |||
| 50 | ASSERT_RTNL(); | ||
| 51 | |||
| 52 | if (!netdev) | ||
| 53 | return -ENODEV; | ||
| 54 | if (!netdev->netdev_ops->ndo_bpf) | ||
| 55 | return -EOPNOTSUPP; | ||
| 56 | |||
| 57 | data->command = cmd; | ||
| 58 | |||
| 59 | return netdev->netdev_ops->ndo_bpf(netdev, data); | ||
| 60 | } | ||
| 61 | |||
| 62 | int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env) | ||
| 63 | { | ||
| 64 | struct netdev_bpf data = {}; | ||
| 65 | int err; | ||
| 66 | |||
| 67 | data.verifier.prog = env->prog; | ||
| 68 | |||
| 69 | rtnl_lock(); | ||
| 70 | err = __bpf_offload_ndo(env->prog, BPF_OFFLOAD_VERIFIER_PREP, &data); | ||
| 71 | if (err) | ||
| 72 | goto exit_unlock; | ||
| 73 | |||
| 74 | env->dev_ops = data.verifier.ops; | ||
| 75 | |||
| 76 | env->prog->aux->offload->dev_state = true; | ||
| 77 | env->prog->aux->offload->verifier_running = true; | ||
| 78 | exit_unlock: | ||
| 79 | rtnl_unlock(); | ||
| 80 | return err; | ||
| 81 | } | ||
| 82 | |||
| 83 | static void __bpf_prog_offload_destroy(struct bpf_prog *prog) | ||
| 84 | { | ||
| 85 | struct bpf_dev_offload *offload = prog->aux->offload; | ||
| 86 | struct netdev_bpf data = {}; | ||
| 87 | |||
| 88 | data.offload.prog = prog; | ||
| 89 | |||
| 90 | if (offload->verifier_running) | ||
| 91 | wait_event(offload->verifier_done, !offload->verifier_running); | ||
| 92 | |||
| 93 | if (offload->dev_state) | ||
| 94 | WARN_ON(__bpf_offload_ndo(prog, BPF_OFFLOAD_DESTROY, &data)); | ||
| 95 | |||
| 96 | offload->dev_state = false; | ||
| 97 | list_del_init(&offload->offloads); | ||
| 98 | offload->netdev = NULL; | ||
| 99 | } | ||
| 100 | |||
| 101 | void bpf_prog_offload_destroy(struct bpf_prog *prog) | ||
| 102 | { | ||
| 103 | struct bpf_dev_offload *offload = prog->aux->offload; | ||
| 104 | |||
| 105 | offload->verifier_running = false; | ||
| 106 | wake_up(&offload->verifier_done); | ||
| 107 | |||
| 108 | rtnl_lock(); | ||
| 109 | __bpf_prog_offload_destroy(prog); | ||
| 110 | rtnl_unlock(); | ||
| 111 | |||
| 112 | kfree(offload); | ||
| 113 | } | ||
| 114 | |||
| 115 | static int bpf_prog_offload_translate(struct bpf_prog *prog) | ||
| 116 | { | ||
| 117 | struct bpf_dev_offload *offload = prog->aux->offload; | ||
| 118 | struct netdev_bpf data = {}; | ||
| 119 | int ret; | ||
| 120 | |||
| 121 | data.offload.prog = prog; | ||
| 122 | |||
| 123 | offload->verifier_running = false; | ||
| 124 | wake_up(&offload->verifier_done); | ||
| 125 | |||
| 126 | rtnl_lock(); | ||
| 127 | ret = __bpf_offload_ndo(prog, BPF_OFFLOAD_TRANSLATE, &data); | ||
| 128 | rtnl_unlock(); | ||
| 129 | |||
| 130 | return ret; | ||
| 131 | } | ||
| 132 | |||
| 133 | static unsigned int bpf_prog_warn_on_exec(const void *ctx, | ||
| 134 | const struct bpf_insn *insn) | ||
| 135 | { | ||
| 136 | WARN(1, "attempt to execute device eBPF program on the host!"); | ||
| 137 | return 0; | ||
| 138 | } | ||
| 139 | |||
| 140 | int bpf_prog_offload_compile(struct bpf_prog *prog) | ||
| 141 | { | ||
| 142 | prog->bpf_func = bpf_prog_warn_on_exec; | ||
| 143 | |||
| 144 | return bpf_prog_offload_translate(prog); | ||
| 145 | } | ||
| 146 | |||
| 147 | const struct bpf_prog_ops bpf_offload_prog_ops = { | ||
| 148 | }; | ||
| 149 | |||
| 150 | static int bpf_offload_notification(struct notifier_block *notifier, | ||
| 151 | ulong event, void *ptr) | ||
| 152 | { | ||
| 153 | struct net_device *netdev = netdev_notifier_info_to_dev(ptr); | ||
| 154 | struct bpf_dev_offload *offload, *tmp; | ||
| 155 | |||
| 156 | ASSERT_RTNL(); | ||
| 157 | |||
| 158 | switch (event) { | ||
| 159 | case NETDEV_UNREGISTER: | ||
| 160 | list_for_each_entry_safe(offload, tmp, &bpf_prog_offload_devs, | ||
| 161 | offloads) { | ||
| 162 | if (offload->netdev == netdev) | ||
| 163 | __bpf_prog_offload_destroy(offload->prog); | ||
| 164 | } | ||
| 165 | break; | ||
| 166 | default: | ||
| 167 | break; | ||
| 168 | } | ||
| 169 | return NOTIFY_OK; | ||
| 170 | } | ||
| 171 | |||
| 172 | static struct notifier_block bpf_offload_notifier = { | ||
| 173 | .notifier_call = bpf_offload_notification, | ||
| 174 | }; | ||
| 175 | |||
| 176 | static int __init bpf_offload_init(void) | ||
| 177 | { | ||
| 178 | register_netdevice_notifier(&bpf_offload_notifier); | ||
| 179 | return 0; | ||
| 180 | } | ||
| 181 | |||
| 182 | subsys_initcall(bpf_offload_init); | ||
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 323be2473c4b..1574b9f0f24e 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c | |||
| @@ -824,7 +824,10 @@ static int find_prog_type(enum bpf_prog_type type, struct bpf_prog *prog) | |||
| 824 | if (type >= ARRAY_SIZE(bpf_prog_types) || !bpf_prog_types[type]) | 824 | if (type >= ARRAY_SIZE(bpf_prog_types) || !bpf_prog_types[type]) |
| 825 | return -EINVAL; | 825 | return -EINVAL; |
| 826 | 826 | ||
| 827 | prog->aux->ops = bpf_prog_types[type]; | 827 | if (!bpf_prog_is_dev_bound(prog->aux)) |
| 828 | prog->aux->ops = bpf_prog_types[type]; | ||
| 829 | else | ||
| 830 | prog->aux->ops = &bpf_offload_prog_ops; | ||
| 828 | prog->type = type; | 831 | prog->type = type; |
| 829 | return 0; | 832 | return 0; |
| 830 | } | 833 | } |
| @@ -1054,7 +1057,7 @@ struct bpf_prog *bpf_prog_inc_not_zero(struct bpf_prog *prog) | |||
| 1054 | } | 1057 | } |
| 1055 | EXPORT_SYMBOL_GPL(bpf_prog_inc_not_zero); | 1058 | EXPORT_SYMBOL_GPL(bpf_prog_inc_not_zero); |
| 1056 | 1059 | ||
| 1057 | static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *type) | 1060 | static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *attach_type) |
| 1058 | { | 1061 | { |
| 1059 | struct fd f = fdget(ufd); | 1062 | struct fd f = fdget(ufd); |
| 1060 | struct bpf_prog *prog; | 1063 | struct bpf_prog *prog; |
| @@ -1062,7 +1065,7 @@ static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *type) | |||
| 1062 | prog = ____bpf_prog_get(f); | 1065 | prog = ____bpf_prog_get(f); |
| 1063 | if (IS_ERR(prog)) | 1066 | if (IS_ERR(prog)) |
| 1064 | return prog; | 1067 | return prog; |
| 1065 | if (type && prog->type != *type) { | 1068 | if (attach_type && (prog->type != *attach_type || prog->aux->offload)) { |
| 1066 | prog = ERR_PTR(-EINVAL); | 1069 | prog = ERR_PTR(-EINVAL); |
| 1067 | goto out; | 1070 | goto out; |
| 1068 | } | 1071 | } |
| @@ -1089,7 +1092,7 @@ struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type) | |||
| 1089 | EXPORT_SYMBOL_GPL(bpf_prog_get_type); | 1092 | EXPORT_SYMBOL_GPL(bpf_prog_get_type); |
| 1090 | 1093 | ||
| 1091 | /* last field in 'union bpf_attr' used by this command */ | 1094 | /* last field in 'union bpf_attr' used by this command */ |
| 1092 | #define BPF_PROG_LOAD_LAST_FIELD prog_name | 1095 | #define BPF_PROG_LOAD_LAST_FIELD prog_target_ifindex |
| 1093 | 1096 | ||
| 1094 | static int bpf_prog_load(union bpf_attr *attr) | 1097 | static int bpf_prog_load(union bpf_attr *attr) |
| 1095 | { | 1098 | { |
| @@ -1152,6 +1155,12 @@ static int bpf_prog_load(union bpf_attr *attr) | |||
| 1152 | atomic_set(&prog->aux->refcnt, 1); | 1155 | atomic_set(&prog->aux->refcnt, 1); |
| 1153 | prog->gpl_compatible = is_gpl ? 1 : 0; | 1156 | prog->gpl_compatible = is_gpl ? 1 : 0; |
| 1154 | 1157 | ||
| 1158 | if (attr->prog_target_ifindex) { | ||
| 1159 | err = bpf_prog_offload_init(prog, attr); | ||
| 1160 | if (err) | ||
| 1161 | goto free_prog; | ||
| 1162 | } | ||
| 1163 | |||
| 1155 | /* find program type: socket_filter vs tracing_filter */ | 1164 | /* find program type: socket_filter vs tracing_filter */ |
| 1156 | err = find_prog_type(type, prog); | 1165 | err = find_prog_type(type, prog); |
| 1157 | if (err < 0) | 1166 | if (err < 0) |
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 04357ad5a812..51aabb32ad67 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c | |||
| @@ -3736,10 +3736,13 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) | |||
| 3736 | static int ext_analyzer_insn_hook(struct bpf_verifier_env *env, | 3736 | static int ext_analyzer_insn_hook(struct bpf_verifier_env *env, |
| 3737 | int insn_idx, int prev_insn_idx) | 3737 | int insn_idx, int prev_insn_idx) |
| 3738 | { | 3738 | { |
| 3739 | if (!env->analyzer_ops || !env->analyzer_ops->insn_hook) | 3739 | if (env->analyzer_ops && env->analyzer_ops->insn_hook) |
| 3740 | return 0; | 3740 | return env->analyzer_ops->insn_hook(env, insn_idx, |
| 3741 | prev_insn_idx); | ||
| 3742 | if (env->dev_ops && env->dev_ops->insn_hook) | ||
| 3743 | return env->dev_ops->insn_hook(env, insn_idx, prev_insn_idx); | ||
| 3741 | 3744 | ||
| 3742 | return env->analyzer_ops->insn_hook(env, insn_idx, prev_insn_idx); | 3745 | return 0; |
| 3743 | } | 3746 | } |
| 3744 | 3747 | ||
| 3745 | static int do_check(struct bpf_verifier_env *env) | 3748 | static int do_check(struct bpf_verifier_env *env) |
| @@ -4516,6 +4519,12 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) | |||
| 4516 | if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) | 4519 | if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) |
| 4517 | env->strict_alignment = true; | 4520 | env->strict_alignment = true; |
| 4518 | 4521 | ||
| 4522 | if (env->prog->aux->offload) { | ||
| 4523 | ret = bpf_prog_offload_verifier_prep(env); | ||
| 4524 | if (ret) | ||
| 4525 | goto err_unlock; | ||
| 4526 | } | ||
| 4527 | |||
| 4519 | ret = replace_map_fd_with_map_ptr(env); | 4528 | ret = replace_map_fd_with_map_ptr(env); |
| 4520 | if (ret < 0) | 4529 | if (ret < 0) |
| 4521 | goto skip_full_check; | 4530 | goto skip_full_check; |
