diff options
| author | Daniel Borkmann <daniel@iogearbox.net> | 2017-12-31 10:12:24 -0500 |
|---|---|---|
| committer | Daniel Borkmann <daniel@iogearbox.net> | 2017-12-31 10:12:24 -0500 |
| commit | 5620e1a8e2e6f96bdb72abfd68a56ce8cb94dd4d (patch) | |
| tree | 1028ca0b34c579067d321d7ac10ce13606b66346 | |
| parent | fb982666e380c1632a74495b68b3c33a66e76430 (diff) | |
| parent | 752d7b4501c250bead233ab041738db84436b1af (diff) | |
Merge branch 'bpf-offload-report-dev'
Jakub Kicinski says:
====================
This series is a redo of reporting offload device information to
user space after the first attempt did not take into account name
spaces. As requested by Kirill offloads are now protected by an
r/w sem. This allows us to remove the workqueue and free the
offload state fully when device is removed (suggested by Alexei).
Net namespace is reported with a device/inode pair.
The accompanying bpftool support is placed in common code because
maps will have very similar info. Note that the UAPI information
can't be nicely encapsulated into a struct, because in case we
need to grow the device information the new fields will have to
be added at the end of struct bpf_prog_info, we can't grow
structures in the middle of bpf_prog_info.
v3:
- use dev_get_by_index();
- redo ns code (new patch 6).
v2:
- rework the locking in patch 1 (use RCU instead of locking
dependencies);
- grab RTNL for a short time in patch 6;
- minor update to the test in patch 8.
====================
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
| -rw-r--r-- | drivers/net/ethernet/netronome/nfp/bpf/main.h | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/netronome/nfp/bpf/verifier.c | 2 | ||||
| -rw-r--r-- | drivers/net/netdevsim/bpf.c | 2 | ||||
| -rw-r--r-- | fs/nsfs.c | 29 | ||||
| -rw-r--r-- | include/linux/bpf.h | 16 | ||||
| -rw-r--r-- | include/linux/bpf_verifier.h | 16 | ||||
| -rw-r--r-- | include/linux/netdevice.h | 4 | ||||
| -rw-r--r-- | include/linux/proc_ns.h | 3 | ||||
| -rw-r--r-- | include/uapi/linux/bpf.h | 3 | ||||
| -rw-r--r-- | kernel/bpf/offload.c | 147 | ||||
| -rw-r--r-- | kernel/bpf/syscall.c | 19 | ||||
| -rw-r--r-- | kernel/bpf/verifier.c | 20 | ||||
| -rw-r--r-- | tools/bpf/bpftool/common.c | 52 | ||||
| -rw-r--r-- | tools/bpf/bpftool/main.h | 2 | ||||
| -rw-r--r-- | tools/bpf/bpftool/prog.c | 3 | ||||
| -rw-r--r-- | tools/include/uapi/linux/bpf.h | 3 | ||||
| -rwxr-xr-x | tools/testing/selftests/bpf/test_offload.py | 112 |
17 files changed, 346 insertions, 89 deletions
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.h b/drivers/net/ethernet/netronome/nfp/bpf/main.h index aae1be9ed056..89a9b6393882 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.h +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.h | |||
| @@ -238,7 +238,7 @@ struct nfp_bpf_vnic { | |||
| 238 | 238 | ||
| 239 | int nfp_bpf_jit(struct nfp_prog *prog); | 239 | int nfp_bpf_jit(struct nfp_prog *prog); |
| 240 | 240 | ||
| 241 | extern const struct bpf_ext_analyzer_ops nfp_bpf_analyzer_ops; | 241 | extern const struct bpf_prog_offload_ops nfp_bpf_analyzer_ops; |
| 242 | 242 | ||
| 243 | struct netdev_bpf; | 243 | struct netdev_bpf; |
| 244 | struct nfp_app; | 244 | struct nfp_app; |
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c index 9c2608445bd8..d8870c2f11f3 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c | |||
| @@ -260,6 +260,6 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx) | |||
| 260 | return 0; | 260 | return 0; |
| 261 | } | 261 | } |
| 262 | 262 | ||
| 263 | const struct bpf_ext_analyzer_ops nfp_bpf_analyzer_ops = { | 263 | const struct bpf_prog_offload_ops nfp_bpf_analyzer_ops = { |
| 264 | .insn_hook = nfp_verify_insn, | 264 | .insn_hook = nfp_verify_insn, |
| 265 | }; | 265 | }; |
diff --git a/drivers/net/netdevsim/bpf.c b/drivers/net/netdevsim/bpf.c index a243fa7ae02f..5134d5c1306c 100644 --- a/drivers/net/netdevsim/bpf.c +++ b/drivers/net/netdevsim/bpf.c | |||
| @@ -66,7 +66,7 @@ nsim_bpf_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn) | |||
| 66 | return 0; | 66 | return 0; |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | static const struct bpf_ext_analyzer_ops nsim_bpf_analyzer_ops = { | 69 | static const struct bpf_prog_offload_ops nsim_bpf_analyzer_ops = { |
| 70 | .insn_hook = nsim_bpf_verify_insn, | 70 | .insn_hook = nsim_bpf_verify_insn, |
| 71 | }; | 71 | }; |
| 72 | 72 | ||
| @@ -103,14 +103,14 @@ slow: | |||
| 103 | goto got_it; | 103 | goto got_it; |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | void *ns_get_path(struct path *path, struct task_struct *task, | 106 | void *ns_get_path_cb(struct path *path, ns_get_path_helper_t *ns_get_cb, |
| 107 | const struct proc_ns_operations *ns_ops) | 107 | void *private_data) |
| 108 | { | 108 | { |
| 109 | struct ns_common *ns; | 109 | struct ns_common *ns; |
| 110 | void *ret; | 110 | void *ret; |
| 111 | 111 | ||
| 112 | again: | 112 | again: |
| 113 | ns = ns_ops->get(task); | 113 | ns = ns_get_cb(private_data); |
| 114 | if (!ns) | 114 | if (!ns) |
| 115 | return ERR_PTR(-ENOENT); | 115 | return ERR_PTR(-ENOENT); |
| 116 | 116 | ||
| @@ -120,6 +120,29 @@ again: | |||
| 120 | return ret; | 120 | return ret; |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | struct ns_get_path_task_args { | ||
| 124 | const struct proc_ns_operations *ns_ops; | ||
| 125 | struct task_struct *task; | ||
| 126 | }; | ||
| 127 | |||
| 128 | static struct ns_common *ns_get_path_task(void *private_data) | ||
| 129 | { | ||
| 130 | struct ns_get_path_task_args *args = private_data; | ||
| 131 | |||
| 132 | return args->ns_ops->get(args->task); | ||
| 133 | } | ||
| 134 | |||
| 135 | void *ns_get_path(struct path *path, struct task_struct *task, | ||
| 136 | const struct proc_ns_operations *ns_ops) | ||
| 137 | { | ||
| 138 | struct ns_get_path_task_args args = { | ||
| 139 | .ns_ops = ns_ops, | ||
| 140 | .task = task, | ||
| 141 | }; | ||
| 142 | |||
| 143 | return ns_get_path_cb(path, ns_get_path_task, &args); | ||
| 144 | } | ||
| 145 | |||
| 123 | int open_related_ns(struct ns_common *ns, | 146 | int open_related_ns(struct ns_common *ns, |
| 124 | struct ns_common *(*get_ns)(struct ns_common *ns)) | 147 | struct ns_common *(*get_ns)(struct ns_common *ns)) |
| 125 | { | 148 | { |
diff --git a/include/linux/bpf.h b/include/linux/bpf.h index da54ef644fcd..7810ae57b357 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/numa.h> | 17 | #include <linux/numa.h> |
| 18 | #include <linux/wait.h> | 18 | #include <linux/wait.h> |
| 19 | 19 | ||
| 20 | struct bpf_verifier_env; | ||
| 20 | struct perf_event; | 21 | struct perf_event; |
| 21 | struct bpf_prog; | 22 | struct bpf_prog; |
| 22 | struct bpf_map; | 23 | struct bpf_map; |
| @@ -184,14 +185,18 @@ struct bpf_verifier_ops { | |||
| 184 | struct bpf_prog *prog, u32 *target_size); | 185 | struct bpf_prog *prog, u32 *target_size); |
| 185 | }; | 186 | }; |
| 186 | 187 | ||
| 188 | struct bpf_prog_offload_ops { | ||
| 189 | int (*insn_hook)(struct bpf_verifier_env *env, | ||
| 190 | int insn_idx, int prev_insn_idx); | ||
| 191 | }; | ||
| 192 | |||
| 187 | struct bpf_dev_offload { | 193 | struct bpf_dev_offload { |
| 188 | struct bpf_prog *prog; | 194 | struct bpf_prog *prog; |
| 189 | struct net_device *netdev; | 195 | struct net_device *netdev; |
| 190 | void *dev_priv; | 196 | void *dev_priv; |
| 191 | struct list_head offloads; | 197 | struct list_head offloads; |
| 192 | bool dev_state; | 198 | bool dev_state; |
| 193 | bool verifier_running; | 199 | const struct bpf_prog_offload_ops *dev_ops; |
| 194 | wait_queue_head_t verifier_done; | ||
| 195 | }; | 200 | }; |
| 196 | 201 | ||
| 197 | struct bpf_prog_aux { | 202 | struct bpf_prog_aux { |
| @@ -201,6 +206,7 @@ struct bpf_prog_aux { | |||
| 201 | u32 stack_depth; | 206 | u32 stack_depth; |
| 202 | u32 id; | 207 | u32 id; |
| 203 | u32 func_cnt; | 208 | u32 func_cnt; |
| 209 | bool offload_requested; | ||
| 204 | struct bpf_prog **func; | 210 | struct bpf_prog **func; |
| 205 | void *jit_data; /* JIT specific data. arch dependent */ | 211 | void *jit_data; /* JIT specific data. arch dependent */ |
| 206 | struct latch_tree_node ksym_tnode; | 212 | struct latch_tree_node ksym_tnode; |
| @@ -351,6 +357,8 @@ void bpf_prog_put(struct bpf_prog *prog); | |||
| 351 | int __bpf_prog_charge(struct user_struct *user, u32 pages); | 357 | int __bpf_prog_charge(struct user_struct *user, u32 pages); |
| 352 | void __bpf_prog_uncharge(struct user_struct *user, u32 pages); | 358 | void __bpf_prog_uncharge(struct user_struct *user, u32 pages); |
| 353 | 359 | ||
| 360 | void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock); | ||
| 361 | |||
| 354 | struct bpf_map *bpf_map_get_with_uref(u32 ufd); | 362 | struct bpf_map *bpf_map_get_with_uref(u32 ufd); |
| 355 | struct bpf_map *__bpf_map_get(struct fd f); | 363 | struct bpf_map *__bpf_map_get(struct fd f); |
| 356 | struct bpf_map * __must_check bpf_map_inc(struct bpf_map *map, bool uref); | 364 | struct bpf_map * __must_check bpf_map_inc(struct bpf_map *map, bool uref); |
| @@ -523,13 +531,15 @@ static inline struct bpf_prog *bpf_prog_get_type(u32 ufd, | |||
| 523 | 531 | ||
| 524 | int bpf_prog_offload_compile(struct bpf_prog *prog); | 532 | int bpf_prog_offload_compile(struct bpf_prog *prog); |
| 525 | void bpf_prog_offload_destroy(struct bpf_prog *prog); | 533 | void bpf_prog_offload_destroy(struct bpf_prog *prog); |
| 534 | int bpf_prog_offload_info_fill(struct bpf_prog_info *info, | ||
| 535 | struct bpf_prog *prog); | ||
| 526 | 536 | ||
| 527 | #if defined(CONFIG_NET) && defined(CONFIG_BPF_SYSCALL) | 537 | #if defined(CONFIG_NET) && defined(CONFIG_BPF_SYSCALL) |
| 528 | int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr); | 538 | int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr); |
| 529 | 539 | ||
| 530 | static inline bool bpf_prog_is_dev_bound(struct bpf_prog_aux *aux) | 540 | static inline bool bpf_prog_is_dev_bound(struct bpf_prog_aux *aux) |
| 531 | { | 541 | { |
| 532 | return aux->offload; | 542 | return aux->offload_requested; |
| 533 | } | 543 | } |
| 534 | #else | 544 | #else |
| 535 | static inline int bpf_prog_offload_init(struct bpf_prog *prog, | 545 | static inline int bpf_prog_offload_init(struct bpf_prog *prog, |
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 883a35d50cd5..2feb218c001d 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h | |||
| @@ -166,12 +166,6 @@ static inline bool bpf_verifier_log_full(const struct bpf_verifer_log *log) | |||
| 166 | return log->len_used >= log->len_total - 1; | 166 | return log->len_used >= log->len_total - 1; |
| 167 | } | 167 | } |
| 168 | 168 | ||
| 169 | struct bpf_verifier_env; | ||
| 170 | struct bpf_ext_analyzer_ops { | ||
| 171 | int (*insn_hook)(struct bpf_verifier_env *env, | ||
| 172 | int insn_idx, int prev_insn_idx); | ||
| 173 | }; | ||
| 174 | |||
| 175 | #define BPF_MAX_SUBPROGS 256 | 169 | #define BPF_MAX_SUBPROGS 256 |
| 176 | 170 | ||
| 177 | /* single container for all structs | 171 | /* single container for all structs |
| @@ -185,7 +179,6 @@ struct bpf_verifier_env { | |||
| 185 | bool strict_alignment; /* perform strict pointer alignment checks */ | 179 | bool strict_alignment; /* perform strict pointer alignment checks */ |
| 186 | struct bpf_verifier_state *cur_state; /* current verifier state */ | 180 | struct bpf_verifier_state *cur_state; /* current verifier state */ |
| 187 | struct bpf_verifier_state_list **explored_states; /* search pruning optimization */ | 181 | struct bpf_verifier_state_list **explored_states; /* search pruning optimization */ |
| 188 | const struct bpf_ext_analyzer_ops *dev_ops; /* device analyzer ops */ | ||
| 189 | struct bpf_map *used_maps[MAX_USED_MAPS]; /* array of map's used by eBPF program */ | 182 | struct bpf_map *used_maps[MAX_USED_MAPS]; /* array of map's used by eBPF program */ |
| 190 | u32 used_map_cnt; /* number of used maps */ | 183 | u32 used_map_cnt; /* number of used maps */ |
| 191 | u32 id_gen; /* used to generate unique reg IDs */ | 184 | u32 id_gen; /* used to generate unique reg IDs */ |
| @@ -206,13 +199,8 @@ static inline struct bpf_reg_state *cur_regs(struct bpf_verifier_env *env) | |||
| 206 | return cur->frame[cur->curframe]->regs; | 199 | return cur->frame[cur->curframe]->regs; |
| 207 | } | 200 | } |
| 208 | 201 | ||
| 209 | #if defined(CONFIG_NET) && defined(CONFIG_BPF_SYSCALL) | ||
| 210 | int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env); | 202 | int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env); |
| 211 | #else | 203 | int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env, |
| 212 | static inline int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env) | 204 | int insn_idx, int prev_insn_idx); |
| 213 | { | ||
| 214 | return -EOPNOTSUPP; | ||
| 215 | } | ||
| 216 | #endif | ||
| 217 | 205 | ||
| 218 | #endif /* _LINUX_BPF_VERIFIER_H */ | 206 | #endif /* _LINUX_BPF_VERIFIER_H */ |
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 352066e4eeef..49bfc6eec74c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
| @@ -804,7 +804,7 @@ enum bpf_netdev_command { | |||
| 804 | BPF_OFFLOAD_DESTROY, | 804 | BPF_OFFLOAD_DESTROY, |
| 805 | }; | 805 | }; |
| 806 | 806 | ||
| 807 | struct bpf_ext_analyzer_ops; | 807 | struct bpf_prog_offload_ops; |
| 808 | struct netlink_ext_ack; | 808 | struct netlink_ext_ack; |
| 809 | 809 | ||
| 810 | struct netdev_bpf { | 810 | struct netdev_bpf { |
| @@ -826,7 +826,7 @@ struct netdev_bpf { | |||
| 826 | /* BPF_OFFLOAD_VERIFIER_PREP */ | 826 | /* BPF_OFFLOAD_VERIFIER_PREP */ |
| 827 | struct { | 827 | struct { |
| 828 | struct bpf_prog *prog; | 828 | struct bpf_prog *prog; |
| 829 | const struct bpf_ext_analyzer_ops *ops; /* callee set */ | 829 | const struct bpf_prog_offload_ops *ops; /* callee set */ |
| 830 | } verifier; | 830 | } verifier; |
| 831 | /* BPF_OFFLOAD_TRANSLATE, BPF_OFFLOAD_DESTROY */ | 831 | /* BPF_OFFLOAD_TRANSLATE, BPF_OFFLOAD_DESTROY */ |
| 832 | struct { | 832 | struct { |
diff --git a/include/linux/proc_ns.h b/include/linux/proc_ns.h index 2ff18c9840a7..d31cb6215905 100644 --- a/include/linux/proc_ns.h +++ b/include/linux/proc_ns.h | |||
| @@ -78,6 +78,9 @@ extern struct file *proc_ns_fget(int fd); | |||
| 78 | #define get_proc_ns(inode) ((struct ns_common *)(inode)->i_private) | 78 | #define get_proc_ns(inode) ((struct ns_common *)(inode)->i_private) |
| 79 | extern void *ns_get_path(struct path *path, struct task_struct *task, | 79 | extern void *ns_get_path(struct path *path, struct task_struct *task, |
| 80 | const struct proc_ns_operations *ns_ops); | 80 | const struct proc_ns_operations *ns_ops); |
| 81 | typedef struct ns_common *ns_get_path_helper_t(void *); | ||
| 82 | extern void *ns_get_path_cb(struct path *path, ns_get_path_helper_t ns_get_cb, | ||
| 83 | void *private_data); | ||
| 81 | 84 | ||
| 82 | extern int ns_get_name(char *buf, size_t size, struct task_struct *task, | 85 | extern int ns_get_name(char *buf, size_t size, struct task_struct *task, |
| 83 | const struct proc_ns_operations *ns_ops); | 86 | const struct proc_ns_operations *ns_ops); |
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 69eabfcb9bdb..f2f8b36e2ad4 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h | |||
| @@ -921,6 +921,9 @@ struct bpf_prog_info { | |||
| 921 | __u32 nr_map_ids; | 921 | __u32 nr_map_ids; |
| 922 | __aligned_u64 map_ids; | 922 | __aligned_u64 map_ids; |
| 923 | char name[BPF_OBJ_NAME_LEN]; | 923 | char name[BPF_OBJ_NAME_LEN]; |
| 924 | __u32 ifindex; | ||
| 925 | __u64 netns_dev; | ||
| 926 | __u64 netns_ino; | ||
| 924 | } __attribute__((aligned(8))); | 927 | } __attribute__((aligned(8))); |
| 925 | 928 | ||
| 926 | struct bpf_map_info { | 929 | struct bpf_map_info { |
diff --git a/kernel/bpf/offload.c b/kernel/bpf/offload.c index 8455b89d1bbf..040d4e0edf3f 100644 --- a/kernel/bpf/offload.c +++ b/kernel/bpf/offload.c | |||
| @@ -16,17 +16,22 @@ | |||
| 16 | #include <linux/bpf.h> | 16 | #include <linux/bpf.h> |
| 17 | #include <linux/bpf_verifier.h> | 17 | #include <linux/bpf_verifier.h> |
| 18 | #include <linux/bug.h> | 18 | #include <linux/bug.h> |
| 19 | #include <linux/kdev_t.h> | ||
| 19 | #include <linux/list.h> | 20 | #include <linux/list.h> |
| 20 | #include <linux/netdevice.h> | 21 | #include <linux/netdevice.h> |
| 21 | #include <linux/printk.h> | 22 | #include <linux/printk.h> |
| 23 | #include <linux/proc_ns.h> | ||
| 22 | #include <linux/rtnetlink.h> | 24 | #include <linux/rtnetlink.h> |
| 25 | #include <linux/rwsem.h> | ||
| 23 | 26 | ||
| 24 | /* protected by RTNL */ | 27 | /* Protects bpf_prog_offload_devs and offload members of all progs. |
| 28 | * RTNL lock cannot be taken when holding this lock. | ||
| 29 | */ | ||
| 30 | static DECLARE_RWSEM(bpf_devs_lock); | ||
| 25 | static LIST_HEAD(bpf_prog_offload_devs); | 31 | static LIST_HEAD(bpf_prog_offload_devs); |
| 26 | 32 | ||
| 27 | int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr) | 33 | int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr) |
| 28 | { | 34 | { |
| 29 | struct net *net = current->nsproxy->net_ns; | ||
| 30 | struct bpf_dev_offload *offload; | 35 | struct bpf_dev_offload *offload; |
| 31 | 36 | ||
| 32 | if (attr->prog_type != BPF_PROG_TYPE_SCHED_CLS && | 37 | if (attr->prog_type != BPF_PROG_TYPE_SCHED_CLS && |
| @@ -41,32 +46,40 @@ int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr) | |||
| 41 | return -ENOMEM; | 46 | return -ENOMEM; |
| 42 | 47 | ||
| 43 | offload->prog = prog; | 48 | offload->prog = prog; |
| 44 | init_waitqueue_head(&offload->verifier_done); | ||
| 45 | 49 | ||
| 46 | rtnl_lock(); | 50 | offload->netdev = dev_get_by_index(current->nsproxy->net_ns, |
| 47 | offload->netdev = __dev_get_by_index(net, attr->prog_ifindex); | 51 | attr->prog_ifindex); |
| 48 | if (!offload->netdev) { | 52 | if (!offload->netdev) |
| 49 | rtnl_unlock(); | 53 | goto err_free; |
| 50 | kfree(offload); | ||
| 51 | return -EINVAL; | ||
| 52 | } | ||
| 53 | 54 | ||
| 55 | down_write(&bpf_devs_lock); | ||
| 56 | if (offload->netdev->reg_state != NETREG_REGISTERED) | ||
| 57 | goto err_unlock; | ||
| 54 | prog->aux->offload = offload; | 58 | prog->aux->offload = offload; |
| 55 | list_add_tail(&offload->offloads, &bpf_prog_offload_devs); | 59 | list_add_tail(&offload->offloads, &bpf_prog_offload_devs); |
| 56 | rtnl_unlock(); | 60 | dev_put(offload->netdev); |
| 61 | up_write(&bpf_devs_lock); | ||
| 57 | 62 | ||
| 58 | return 0; | 63 | return 0; |
| 64 | err_unlock: | ||
| 65 | up_write(&bpf_devs_lock); | ||
| 66 | dev_put(offload->netdev); | ||
| 67 | err_free: | ||
| 68 | kfree(offload); | ||
| 69 | return -EINVAL; | ||
| 59 | } | 70 | } |
| 60 | 71 | ||
| 61 | static int __bpf_offload_ndo(struct bpf_prog *prog, enum bpf_netdev_command cmd, | 72 | static int __bpf_offload_ndo(struct bpf_prog *prog, enum bpf_netdev_command cmd, |
| 62 | struct netdev_bpf *data) | 73 | struct netdev_bpf *data) |
| 63 | { | 74 | { |
| 64 | struct net_device *netdev = prog->aux->offload->netdev; | 75 | struct bpf_dev_offload *offload = prog->aux->offload; |
| 76 | struct net_device *netdev; | ||
| 65 | 77 | ||
| 66 | ASSERT_RTNL(); | 78 | ASSERT_RTNL(); |
| 67 | 79 | ||
| 68 | if (!netdev) | 80 | if (!offload) |
| 69 | return -ENODEV; | 81 | return -ENODEV; |
| 82 | netdev = offload->netdev; | ||
| 70 | if (!netdev->netdev_ops->ndo_bpf) | 83 | if (!netdev->netdev_ops->ndo_bpf) |
| 71 | return -EOPNOTSUPP; | 84 | return -EOPNOTSUPP; |
| 72 | 85 | ||
| @@ -87,62 +100,63 @@ int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env) | |||
| 87 | if (err) | 100 | if (err) |
| 88 | goto exit_unlock; | 101 | goto exit_unlock; |
| 89 | 102 | ||
| 90 | env->dev_ops = data.verifier.ops; | 103 | env->prog->aux->offload->dev_ops = data.verifier.ops; |
| 91 | |||
| 92 | env->prog->aux->offload->dev_state = true; | 104 | env->prog->aux->offload->dev_state = true; |
| 93 | env->prog->aux->offload->verifier_running = true; | ||
| 94 | exit_unlock: | 105 | exit_unlock: |
| 95 | rtnl_unlock(); | 106 | rtnl_unlock(); |
| 96 | return err; | 107 | return err; |
| 97 | } | 108 | } |
| 98 | 109 | ||
| 110 | int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env, | ||
| 111 | int insn_idx, int prev_insn_idx) | ||
| 112 | { | ||
| 113 | struct bpf_dev_offload *offload; | ||
| 114 | int ret = -ENODEV; | ||
| 115 | |||
| 116 | down_read(&bpf_devs_lock); | ||
| 117 | offload = env->prog->aux->offload; | ||
| 118 | if (offload) | ||
| 119 | ret = offload->dev_ops->insn_hook(env, insn_idx, prev_insn_idx); | ||
| 120 | up_read(&bpf_devs_lock); | ||
| 121 | |||
| 122 | return ret; | ||
| 123 | } | ||
| 124 | |||
| 99 | static void __bpf_prog_offload_destroy(struct bpf_prog *prog) | 125 | static void __bpf_prog_offload_destroy(struct bpf_prog *prog) |
| 100 | { | 126 | { |
| 101 | struct bpf_dev_offload *offload = prog->aux->offload; | 127 | struct bpf_dev_offload *offload = prog->aux->offload; |
| 102 | struct netdev_bpf data = {}; | 128 | struct netdev_bpf data = {}; |
| 103 | 129 | ||
| 104 | /* Caution - if netdev is destroyed before the program, this function | ||
| 105 | * will be called twice. | ||
| 106 | */ | ||
| 107 | |||
| 108 | data.offload.prog = prog; | 130 | data.offload.prog = prog; |
| 109 | 131 | ||
| 110 | if (offload->verifier_running) | ||
| 111 | wait_event(offload->verifier_done, !offload->verifier_running); | ||
| 112 | |||
| 113 | if (offload->dev_state) | 132 | if (offload->dev_state) |
| 114 | WARN_ON(__bpf_offload_ndo(prog, BPF_OFFLOAD_DESTROY, &data)); | 133 | WARN_ON(__bpf_offload_ndo(prog, BPF_OFFLOAD_DESTROY, &data)); |
| 115 | 134 | ||
| 116 | offload->dev_state = false; | 135 | /* Make sure BPF_PROG_GET_NEXT_ID can't find this dead program */ |
| 136 | bpf_prog_free_id(prog, true); | ||
| 137 | |||
| 117 | list_del_init(&offload->offloads); | 138 | list_del_init(&offload->offloads); |
| 118 | offload->netdev = NULL; | 139 | kfree(offload); |
| 140 | prog->aux->offload = NULL; | ||
| 119 | } | 141 | } |
| 120 | 142 | ||
| 121 | void bpf_prog_offload_destroy(struct bpf_prog *prog) | 143 | void bpf_prog_offload_destroy(struct bpf_prog *prog) |
| 122 | { | 144 | { |
| 123 | struct bpf_dev_offload *offload = prog->aux->offload; | ||
| 124 | |||
| 125 | offload->verifier_running = false; | ||
| 126 | wake_up(&offload->verifier_done); | ||
| 127 | |||
| 128 | rtnl_lock(); | 145 | rtnl_lock(); |
| 129 | __bpf_prog_offload_destroy(prog); | 146 | down_write(&bpf_devs_lock); |
| 147 | if (prog->aux->offload) | ||
| 148 | __bpf_prog_offload_destroy(prog); | ||
| 149 | up_write(&bpf_devs_lock); | ||
| 130 | rtnl_unlock(); | 150 | rtnl_unlock(); |
| 131 | |||
| 132 | kfree(offload); | ||
| 133 | } | 151 | } |
| 134 | 152 | ||
| 135 | static int bpf_prog_offload_translate(struct bpf_prog *prog) | 153 | static int bpf_prog_offload_translate(struct bpf_prog *prog) |
| 136 | { | 154 | { |
| 137 | struct bpf_dev_offload *offload = prog->aux->offload; | ||
| 138 | struct netdev_bpf data = {}; | 155 | struct netdev_bpf data = {}; |
| 139 | int ret; | 156 | int ret; |
| 140 | 157 | ||
| 141 | data.offload.prog = prog; | 158 | data.offload.prog = prog; |
| 142 | 159 | ||
| 143 | offload->verifier_running = false; | ||
| 144 | wake_up(&offload->verifier_done); | ||
| 145 | |||
| 146 | rtnl_lock(); | 160 | rtnl_lock(); |
| 147 | ret = __bpf_offload_ndo(prog, BPF_OFFLOAD_TRANSLATE, &data); | 161 | ret = __bpf_offload_ndo(prog, BPF_OFFLOAD_TRANSLATE, &data); |
| 148 | rtnl_unlock(); | 162 | rtnl_unlock(); |
| @@ -164,6 +178,63 @@ int bpf_prog_offload_compile(struct bpf_prog *prog) | |||
| 164 | return bpf_prog_offload_translate(prog); | 178 | return bpf_prog_offload_translate(prog); |
| 165 | } | 179 | } |
| 166 | 180 | ||
| 181 | struct ns_get_path_bpf_prog_args { | ||
| 182 | struct bpf_prog *prog; | ||
| 183 | struct bpf_prog_info *info; | ||
| 184 | }; | ||
| 185 | |||
| 186 | static struct ns_common *bpf_prog_offload_info_fill_ns(void *private_data) | ||
| 187 | { | ||
| 188 | struct ns_get_path_bpf_prog_args *args = private_data; | ||
| 189 | struct bpf_prog_aux *aux = args->prog->aux; | ||
| 190 | struct ns_common *ns; | ||
| 191 | struct net *net; | ||
| 192 | |||
| 193 | rtnl_lock(); | ||
| 194 | down_read(&bpf_devs_lock); | ||
| 195 | |||
| 196 | if (aux->offload) { | ||
| 197 | args->info->ifindex = aux->offload->netdev->ifindex; | ||
| 198 | net = dev_net(aux->offload->netdev); | ||
| 199 | get_net(net); | ||
| 200 | ns = &net->ns; | ||
| 201 | } else { | ||
| 202 | args->info->ifindex = 0; | ||
| 203 | ns = NULL; | ||
| 204 | } | ||
| 205 | |||
| 206 | up_read(&bpf_devs_lock); | ||
| 207 | rtnl_unlock(); | ||
| 208 | |||
| 209 | return ns; | ||
| 210 | } | ||
| 211 | |||
| 212 | int bpf_prog_offload_info_fill(struct bpf_prog_info *info, | ||
| 213 | struct bpf_prog *prog) | ||
| 214 | { | ||
| 215 | struct ns_get_path_bpf_prog_args args = { | ||
| 216 | .prog = prog, | ||
| 217 | .info = info, | ||
| 218 | }; | ||
| 219 | struct inode *ns_inode; | ||
| 220 | struct path ns_path; | ||
| 221 | void *res; | ||
| 222 | |||
| 223 | res = ns_get_path_cb(&ns_path, bpf_prog_offload_info_fill_ns, &args); | ||
| 224 | if (IS_ERR(res)) { | ||
| 225 | if (!info->ifindex) | ||
| 226 | return -ENODEV; | ||
| 227 | return PTR_ERR(res); | ||
| 228 | } | ||
| 229 | |||
| 230 | ns_inode = ns_path.dentry->d_inode; | ||
| 231 | info->netns_dev = new_encode_dev(ns_inode->i_sb->s_dev); | ||
| 232 | info->netns_ino = ns_inode->i_ino; | ||
| 233 | path_put(&ns_path); | ||
| 234 | |||
| 235 | return 0; | ||
| 236 | } | ||
| 237 | |||
| 167 | const struct bpf_prog_ops bpf_offload_prog_ops = { | 238 | const struct bpf_prog_ops bpf_offload_prog_ops = { |
| 168 | }; | 239 | }; |
| 169 | 240 | ||
| @@ -181,11 +252,13 @@ static int bpf_offload_notification(struct notifier_block *notifier, | |||
| 181 | if (netdev->reg_state != NETREG_UNREGISTERING) | 252 | if (netdev->reg_state != NETREG_UNREGISTERING) |
| 182 | break; | 253 | break; |
| 183 | 254 | ||
| 255 | down_write(&bpf_devs_lock); | ||
| 184 | list_for_each_entry_safe(offload, tmp, &bpf_prog_offload_devs, | 256 | list_for_each_entry_safe(offload, tmp, &bpf_prog_offload_devs, |
| 185 | offloads) { | 257 | offloads) { |
| 186 | if (offload->netdev == netdev) | 258 | if (offload->netdev == netdev) |
| 187 | __bpf_prog_offload_destroy(offload->prog); | 259 | __bpf_prog_offload_destroy(offload->prog); |
| 188 | } | 260 | } |
| 261 | up_write(&bpf_devs_lock); | ||
| 189 | break; | 262 | break; |
| 190 | default: | 263 | default: |
| 191 | break; | 264 | break; |
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 007802c5ca7d..ebf0fb23e237 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c | |||
| @@ -905,9 +905,13 @@ static int bpf_prog_alloc_id(struct bpf_prog *prog) | |||
| 905 | return id > 0 ? 0 : id; | 905 | return id > 0 ? 0 : id; |
| 906 | } | 906 | } |
| 907 | 907 | ||
| 908 | static void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock) | 908 | void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock) |
| 909 | { | 909 | { |
| 910 | /* cBPF to eBPF migrations are currently not in the idr store. */ | 910 | /* cBPF to eBPF migrations are currently not in the idr store. |
| 911 | * Offloaded programs are removed from the store when their device | ||
| 912 | * disappears - even if someone grabs an fd to them they are unusable, | ||
| 913 | * simply waiting for refcnt to drop to be freed. | ||
| 914 | */ | ||
| 911 | if (!prog->aux->id) | 915 | if (!prog->aux->id) |
| 912 | return; | 916 | return; |
| 913 | 917 | ||
| @@ -917,6 +921,7 @@ static void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock) | |||
| 917 | __acquire(&prog_idr_lock); | 921 | __acquire(&prog_idr_lock); |
| 918 | 922 | ||
| 919 | idr_remove(&prog_idr, prog->aux->id); | 923 | idr_remove(&prog_idr, prog->aux->id); |
| 924 | prog->aux->id = 0; | ||
| 920 | 925 | ||
| 921 | if (do_idr_lock) | 926 | if (do_idr_lock) |
| 922 | spin_unlock_bh(&prog_idr_lock); | 927 | spin_unlock_bh(&prog_idr_lock); |
| @@ -1157,6 +1162,8 @@ static int bpf_prog_load(union bpf_attr *attr) | |||
| 1157 | if (!prog) | 1162 | if (!prog) |
| 1158 | return -ENOMEM; | 1163 | return -ENOMEM; |
| 1159 | 1164 | ||
| 1165 | prog->aux->offload_requested = !!attr->prog_ifindex; | ||
| 1166 | |||
| 1160 | err = security_bpf_prog_alloc(prog->aux); | 1167 | err = security_bpf_prog_alloc(prog->aux); |
| 1161 | if (err) | 1168 | if (err) |
| 1162 | goto free_prog_nouncharge; | 1169 | goto free_prog_nouncharge; |
| @@ -1178,7 +1185,7 @@ static int bpf_prog_load(union bpf_attr *attr) | |||
| 1178 | atomic_set(&prog->aux->refcnt, 1); | 1185 | atomic_set(&prog->aux->refcnt, 1); |
| 1179 | prog->gpl_compatible = is_gpl ? 1 : 0; | 1186 | prog->gpl_compatible = is_gpl ? 1 : 0; |
| 1180 | 1187 | ||
| 1181 | if (attr->prog_ifindex) { | 1188 | if (bpf_prog_is_dev_bound(prog->aux)) { |
| 1182 | err = bpf_prog_offload_init(prog, attr); | 1189 | err = bpf_prog_offload_init(prog, attr); |
| 1183 | if (err) | 1190 | if (err) |
| 1184 | goto free_prog; | 1191 | goto free_prog; |
| @@ -1700,6 +1707,12 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog, | |||
| 1700 | return -EFAULT; | 1707 | return -EFAULT; |
| 1701 | } | 1708 | } |
| 1702 | 1709 | ||
| 1710 | if (bpf_prog_is_dev_bound(prog->aux)) { | ||
| 1711 | err = bpf_prog_offload_info_fill(&info, prog); | ||
| 1712 | if (err) | ||
| 1713 | return err; | ||
| 1714 | } | ||
| 1715 | |||
| 1703 | done: | 1716 | done: |
| 1704 | if (copy_to_user(uinfo, &info, info_len) || | 1717 | if (copy_to_user(uinfo, &info, info_len) || |
| 1705 | put_user(info_len, &uattr->info.info_len)) | 1718 | put_user(info_len, &uattr->info.info_len)) |
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 98d8637cf70d..a2b211262c25 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c | |||
| @@ -4438,15 +4438,6 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) | |||
| 4438 | return 0; | 4438 | return 0; |
| 4439 | } | 4439 | } |
| 4440 | 4440 | ||
| 4441 | static int ext_analyzer_insn_hook(struct bpf_verifier_env *env, | ||
| 4442 | int insn_idx, int prev_insn_idx) | ||
| 4443 | { | ||
| 4444 | if (env->dev_ops && env->dev_ops->insn_hook) | ||
| 4445 | return env->dev_ops->insn_hook(env, insn_idx, prev_insn_idx); | ||
| 4446 | |||
| 4447 | return 0; | ||
| 4448 | } | ||
| 4449 | |||
| 4450 | static int do_check(struct bpf_verifier_env *env) | 4441 | static int do_check(struct bpf_verifier_env *env) |
| 4451 | { | 4442 | { |
| 4452 | struct bpf_verifier_state *state; | 4443 | struct bpf_verifier_state *state; |
| @@ -4531,9 +4522,12 @@ static int do_check(struct bpf_verifier_env *env) | |||
| 4531 | print_bpf_insn(&cbs, env, insn, env->allow_ptr_leaks); | 4522 | print_bpf_insn(&cbs, env, insn, env->allow_ptr_leaks); |
| 4532 | } | 4523 | } |
| 4533 | 4524 | ||
| 4534 | err = ext_analyzer_insn_hook(env, insn_idx, prev_insn_idx); | 4525 | if (bpf_prog_is_dev_bound(env->prog->aux)) { |
| 4535 | if (err) | 4526 | err = bpf_prog_offload_verify_insn(env, insn_idx, |
| 4536 | return err; | 4527 | prev_insn_idx); |
| 4528 | if (err) | ||
| 4529 | return err; | ||
| 4530 | } | ||
| 4537 | 4531 | ||
| 4538 | regs = cur_regs(env); | 4532 | regs = cur_regs(env); |
| 4539 | env->insn_aux_data[insn_idx].seen = true; | 4533 | env->insn_aux_data[insn_idx].seen = true; |
| @@ -5463,7 +5457,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) | |||
| 5463 | if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) | 5457 | if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) |
| 5464 | env->strict_alignment = true; | 5458 | env->strict_alignment = true; |
| 5465 | 5459 | ||
| 5466 | if (env->prog->aux->offload) { | 5460 | if (bpf_prog_is_dev_bound(env->prog->aux)) { |
| 5467 | ret = bpf_prog_offload_verifier_prep(env); | 5461 | ret = bpf_prog_offload_verifier_prep(env); |
| 5468 | if (ret) | 5462 | if (ret) |
| 5469 | goto err_unlock; | 5463 | goto err_unlock; |
diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index b62c94e3997a..6601c95a9258 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c | |||
| @@ -44,7 +44,9 @@ | |||
| 44 | #include <unistd.h> | 44 | #include <unistd.h> |
| 45 | #include <linux/limits.h> | 45 | #include <linux/limits.h> |
| 46 | #include <linux/magic.h> | 46 | #include <linux/magic.h> |
| 47 | #include <net/if.h> | ||
| 47 | #include <sys/mount.h> | 48 | #include <sys/mount.h> |
| 49 | #include <sys/stat.h> | ||
| 48 | #include <sys/types.h> | 50 | #include <sys/types.h> |
| 49 | #include <sys/vfs.h> | 51 | #include <sys/vfs.h> |
| 50 | 52 | ||
| @@ -412,3 +414,53 @@ void delete_pinned_obj_table(struct pinned_obj_table *tab) | |||
| 412 | free(obj); | 414 | free(obj); |
| 413 | } | 415 | } |
| 414 | } | 416 | } |
| 417 | |||
| 418 | static char * | ||
| 419 | ifindex_to_name_ns(__u32 ifindex, __u32 ns_dev, __u32 ns_ino, char *buf) | ||
| 420 | { | ||
| 421 | struct stat st; | ||
| 422 | int err; | ||
| 423 | |||
| 424 | err = stat("/proc/self/ns/net", &st); | ||
| 425 | if (err) { | ||
| 426 | p_err("Can't stat /proc/self: %s", strerror(errno)); | ||
| 427 | return NULL; | ||
| 428 | } | ||
| 429 | |||
| 430 | if (st.st_dev != ns_dev || st.st_ino != ns_ino) | ||
| 431 | return NULL; | ||
| 432 | |||
| 433 | return if_indextoname(ifindex, buf); | ||
| 434 | } | ||
| 435 | |||
| 436 | void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode) | ||
| 437 | { | ||
| 438 | char name[IF_NAMESIZE]; | ||
| 439 | |||
| 440 | if (!ifindex) | ||
| 441 | return; | ||
| 442 | |||
| 443 | printf(" dev "); | ||
| 444 | if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name)) | ||
| 445 | printf("%s", name); | ||
| 446 | else | ||
| 447 | printf("ifindex %u ns_dev %llu ns_ino %llu", | ||
| 448 | ifindex, ns_dev, ns_inode); | ||
| 449 | } | ||
| 450 | |||
| 451 | void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode) | ||
| 452 | { | ||
| 453 | char name[IF_NAMESIZE]; | ||
| 454 | |||
| 455 | if (!ifindex) | ||
| 456 | return; | ||
| 457 | |||
| 458 | jsonw_name(json_wtr, "dev"); | ||
| 459 | jsonw_start_object(json_wtr); | ||
| 460 | jsonw_uint_field(json_wtr, "ifindex", ifindex); | ||
| 461 | jsonw_uint_field(json_wtr, "ns_dev", ns_dev); | ||
| 462 | jsonw_uint_field(json_wtr, "ns_inode", ns_inode); | ||
| 463 | if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name)) | ||
| 464 | jsonw_string_field(json_wtr, "ifname", name); | ||
| 465 | jsonw_end_object(json_wtr); | ||
| 466 | } | ||
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 8f6d3cac0347..65b526fe6e7e 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h | |||
| @@ -96,6 +96,8 @@ struct pinned_obj { | |||
| 96 | int build_pinned_obj_table(struct pinned_obj_table *table, | 96 | int build_pinned_obj_table(struct pinned_obj_table *table, |
| 97 | enum bpf_obj_type type); | 97 | enum bpf_obj_type type); |
| 98 | void delete_pinned_obj_table(struct pinned_obj_table *tab); | 98 | void delete_pinned_obj_table(struct pinned_obj_table *tab); |
| 99 | void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode); | ||
| 100 | void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode); | ||
| 99 | 101 | ||
| 100 | struct cmd { | 102 | struct cmd { |
| 101 | const char *cmd; | 103 | const char *cmd; |
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index fd0873178503..98f871ed53d6 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c | |||
| @@ -230,6 +230,8 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) | |||
| 230 | info->tag[0], info->tag[1], info->tag[2], info->tag[3], | 230 | info->tag[0], info->tag[1], info->tag[2], info->tag[3], |
| 231 | info->tag[4], info->tag[5], info->tag[6], info->tag[7]); | 231 | info->tag[4], info->tag[5], info->tag[6], info->tag[7]); |
| 232 | 232 | ||
| 233 | print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); | ||
| 234 | |||
| 233 | if (info->load_time) { | 235 | if (info->load_time) { |
| 234 | char buf[32]; | 236 | char buf[32]; |
| 235 | 237 | ||
| @@ -287,6 +289,7 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd) | |||
| 287 | 289 | ||
| 288 | printf("tag "); | 290 | printf("tag "); |
| 289 | fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); | 291 | fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); |
| 292 | print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); | ||
| 290 | printf("\n"); | 293 | printf("\n"); |
| 291 | 294 | ||
| 292 | if (info->load_time) { | 295 | if (info->load_time) { |
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index db1b0923a308..4e8c60acfa32 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h | |||
| @@ -921,6 +921,9 @@ struct bpf_prog_info { | |||
| 921 | __u32 nr_map_ids; | 921 | __u32 nr_map_ids; |
| 922 | __aligned_u64 map_ids; | 922 | __aligned_u64 map_ids; |
| 923 | char name[BPF_OBJ_NAME_LEN]; | 923 | char name[BPF_OBJ_NAME_LEN]; |
| 924 | __u32 ifindex; | ||
| 925 | __u64 netns_dev; | ||
| 926 | __u64 netns_ino; | ||
| 924 | } __attribute__((aligned(8))); | 927 | } __attribute__((aligned(8))); |
| 925 | 928 | ||
| 926 | struct bpf_map_info { | 929 | struct bpf_map_info { |
diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py index c940505c2978..e3c750f17cb8 100755 --- a/tools/testing/selftests/bpf/test_offload.py +++ b/tools/testing/selftests/bpf/test_offload.py | |||
| @@ -18,6 +18,8 @@ import argparse | |||
| 18 | import json | 18 | import json |
| 19 | import os | 19 | import os |
| 20 | import pprint | 20 | import pprint |
| 21 | import random | ||
| 22 | import string | ||
| 21 | import subprocess | 23 | import subprocess |
| 22 | import time | 24 | import time |
| 23 | 25 | ||
| @@ -27,6 +29,7 @@ bpf_test_dir = os.path.dirname(os.path.realpath(__file__)) | |||
| 27 | pp = pprint.PrettyPrinter() | 29 | pp = pprint.PrettyPrinter() |
| 28 | devs = [] # devices we created for clean up | 30 | devs = [] # devices we created for clean up |
| 29 | files = [] # files to be removed | 31 | files = [] # files to be removed |
| 32 | netns = [] # net namespaces to be removed | ||
| 30 | 33 | ||
| 31 | def log_get_sec(level=0): | 34 | def log_get_sec(level=0): |
| 32 | return "*" * (log_level + level) | 35 | return "*" * (log_level + level) |
| @@ -128,22 +131,25 @@ def rm(f): | |||
| 128 | if f in files: | 131 | if f in files: |
| 129 | files.remove(f) | 132 | files.remove(f) |
| 130 | 133 | ||
| 131 | def tool(name, args, flags, JSON=True, fail=True): | 134 | def tool(name, args, flags, JSON=True, ns="", fail=True): |
| 132 | params = "" | 135 | params = "" |
| 133 | if JSON: | 136 | if JSON: |
| 134 | params += "%s " % (flags["json"]) | 137 | params += "%s " % (flags["json"]) |
| 135 | 138 | ||
| 136 | ret, out = cmd(name + " " + params + args, fail=fail) | 139 | if ns != "": |
| 140 | ns = "ip netns exec %s " % (ns) | ||
| 141 | |||
| 142 | ret, out = cmd(ns + name + " " + params + args, fail=fail) | ||
| 137 | if JSON and len(out.strip()) != 0: | 143 | if JSON and len(out.strip()) != 0: |
| 138 | return ret, json.loads(out) | 144 | return ret, json.loads(out) |
| 139 | else: | 145 | else: |
| 140 | return ret, out | 146 | return ret, out |
| 141 | 147 | ||
| 142 | def bpftool(args, JSON=True, fail=True): | 148 | def bpftool(args, JSON=True, ns="", fail=True): |
| 143 | return tool("bpftool", args, {"json":"-p"}, JSON=JSON, fail=fail) | 149 | return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail) |
| 144 | 150 | ||
| 145 | def bpftool_prog_list(expected=None): | 151 | def bpftool_prog_list(expected=None, ns=""): |
| 146 | _, progs = bpftool("prog show", JSON=True, fail=True) | 152 | _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True) |
| 147 | if expected is not None: | 153 | if expected is not None: |
| 148 | if len(progs) != expected: | 154 | if len(progs) != expected: |
| 149 | fail(True, "%d BPF programs loaded, expected %d" % | 155 | fail(True, "%d BPF programs loaded, expected %d" % |
| @@ -158,13 +164,13 @@ def bpftool_prog_list_wait(expected=0, n_retry=20): | |||
| 158 | time.sleep(0.05) | 164 | time.sleep(0.05) |
| 159 | raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs)) | 165 | raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs)) |
| 160 | 166 | ||
| 161 | def ip(args, force=False, JSON=True, fail=True): | 167 | def ip(args, force=False, JSON=True, ns="", fail=True): |
| 162 | if force: | 168 | if force: |
| 163 | args = "-force " + args | 169 | args = "-force " + args |
| 164 | return tool("ip", args, {"json":"-j"}, JSON=JSON, fail=fail) | 170 | return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns, fail=fail) |
| 165 | 171 | ||
| 166 | def tc(args, JSON=True, fail=True): | 172 | def tc(args, JSON=True, ns="", fail=True): |
| 167 | return tool("tc", args, {"json":"-p"}, JSON=JSON, fail=fail) | 173 | return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail) |
| 168 | 174 | ||
| 169 | def ethtool(dev, opt, args, fail=True): | 175 | def ethtool(dev, opt, args, fail=True): |
| 170 | return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail) | 176 | return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail) |
| @@ -178,6 +184,15 @@ def bpf_pinned(name): | |||
| 178 | def bpf_bytecode(bytecode): | 184 | def bpf_bytecode(bytecode): |
| 179 | return "bytecode \"%s\"" % (bytecode) | 185 | return "bytecode \"%s\"" % (bytecode) |
| 180 | 186 | ||
| 187 | def mknetns(n_retry=10): | ||
| 188 | for i in range(n_retry): | ||
| 189 | name = ''.join([random.choice(string.ascii_letters) for i in range(8)]) | ||
| 190 | ret, _ = ip("netns add %s" % (name), fail=False) | ||
| 191 | if ret == 0: | ||
| 192 | netns.append(name) | ||
| 193 | return name | ||
| 194 | return None | ||
| 195 | |||
| 181 | class DebugfsDir: | 196 | class DebugfsDir: |
| 182 | """ | 197 | """ |
| 183 | Class for accessing DebugFS directories as a dictionary. | 198 | Class for accessing DebugFS directories as a dictionary. |
| @@ -237,6 +252,8 @@ class NetdevSim: | |||
| 237 | self.dev = self._netdevsim_create() | 252 | self.dev = self._netdevsim_create() |
| 238 | devs.append(self) | 253 | devs.append(self) |
| 239 | 254 | ||
| 255 | self.ns = "" | ||
| 256 | |||
| 240 | self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname']) | 257 | self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname']) |
| 241 | self.dfs_refresh() | 258 | self.dfs_refresh() |
| 242 | 259 | ||
| @@ -257,7 +274,7 @@ class NetdevSim: | |||
| 257 | 274 | ||
| 258 | def remove(self): | 275 | def remove(self): |
| 259 | devs.remove(self) | 276 | devs.remove(self) |
| 260 | ip("link del dev %s" % (self.dev["ifname"])) | 277 | ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns) |
| 261 | 278 | ||
| 262 | def dfs_refresh(self): | 279 | def dfs_refresh(self): |
| 263 | self.dfs = DebugfsDir(self.dfs_dir) | 280 | self.dfs = DebugfsDir(self.dfs_dir) |
| @@ -285,6 +302,11 @@ class NetdevSim: | |||
| 285 | time.sleep(0.05) | 302 | time.sleep(0.05) |
| 286 | raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs)) | 303 | raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs)) |
| 287 | 304 | ||
| 305 | def set_ns(self, ns): | ||
| 306 | name = "1" if ns == "" else ns | ||
| 307 | ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns) | ||
| 308 | self.ns = ns | ||
| 309 | |||
| 288 | def set_mtu(self, mtu, fail=True): | 310 | def set_mtu(self, mtu, fail=True): |
| 289 | return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu), | 311 | return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu), |
| 290 | fail=fail) | 312 | fail=fail) |
| @@ -372,6 +394,8 @@ def clean_up(): | |||
| 372 | dev.remove() | 394 | dev.remove() |
| 373 | for f in files: | 395 | for f in files: |
| 374 | cmd("rm -f %s" % (f)) | 396 | cmd("rm -f %s" % (f)) |
| 397 | for ns in netns: | ||
| 398 | cmd("ip netns delete %s" % (ns)) | ||
| 375 | 399 | ||
| 376 | def pin_prog(file_name, idx=0): | 400 | def pin_prog(file_name, idx=0): |
| 377 | progs = bpftool_prog_list(expected=(idx + 1)) | 401 | progs = bpftool_prog_list(expected=(idx + 1)) |
| @@ -381,6 +405,35 @@ def pin_prog(file_name, idx=0): | |||
| 381 | 405 | ||
| 382 | return file_name, bpf_pinned(file_name) | 406 | return file_name, bpf_pinned(file_name) |
| 383 | 407 | ||
| 408 | def check_dev_info(other_ns, ns, pin_file=None, removed=False): | ||
| 409 | if removed: | ||
| 410 | bpftool_prog_list(expected=0) | ||
| 411 | ret, err = bpftool("prog show pin %s" % (pin_file), fail=False) | ||
| 412 | fail(ret == 0, "Showing prog with removed device did not fail") | ||
| 413 | fail(err["error"].find("No such device") == -1, | ||
| 414 | "Showing prog with removed device expected ENODEV, error is %s" % | ||
| 415 | (err["error"])) | ||
| 416 | return | ||
| 417 | progs = bpftool_prog_list(expected=int(not removed), ns=ns) | ||
| 418 | prog = progs[0] | ||
| 419 | |||
| 420 | fail("dev" not in prog.keys(), "Device parameters not reported") | ||
| 421 | dev = prog["dev"] | ||
| 422 | fail("ifindex" not in dev.keys(), "Device parameters not reported") | ||
| 423 | fail("ns_dev" not in dev.keys(), "Device parameters not reported") | ||
| 424 | fail("ns_inode" not in dev.keys(), "Device parameters not reported") | ||
| 425 | |||
| 426 | if not removed and not other_ns: | ||
| 427 | fail("ifname" not in dev.keys(), "Ifname not reported") | ||
| 428 | fail(dev["ifname"] != sim["ifname"], | ||
| 429 | "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"])) | ||
| 430 | else: | ||
| 431 | fail("ifname" in dev.keys(), "Ifname is reported for other ns") | ||
| 432 | if removed: | ||
| 433 | fail(dev["ifindex"] != 0, "Device perameters not zero on removed") | ||
| 434 | fail(dev["ns_dev"] != 0, "Device perameters not zero on removed") | ||
| 435 | fail(dev["ns_inode"] != 0, "Device perameters not zero on removed") | ||
| 436 | |||
| 384 | # Parse command line | 437 | # Parse command line |
| 385 | parser = argparse.ArgumentParser() | 438 | parser = argparse.ArgumentParser() |
| 386 | parser.add_argument("--log", help="output verbose log to given file") | 439 | parser.add_argument("--log", help="output verbose log to given file") |
| @@ -417,6 +470,12 @@ for s in samples: | |||
| 417 | skip(ret != 0, "sample %s/%s not found, please compile it" % | 470 | skip(ret != 0, "sample %s/%s not found, please compile it" % |
| 418 | (bpf_test_dir, s)) | 471 | (bpf_test_dir, s)) |
| 419 | 472 | ||
| 473 | # Check if net namespaces seem to work | ||
| 474 | ns = mknetns() | ||
| 475 | skip(ns is None, "Could not create a net namespace") | ||
| 476 | cmd("ip netns delete %s" % (ns)) | ||
| 477 | netns = [] | ||
| 478 | |||
| 420 | try: | 479 | try: |
| 421 | obj = bpf_obj("sample_ret0.o") | 480 | obj = bpf_obj("sample_ret0.o") |
| 422 | bytecode = bpf_bytecode("1,6 0 0 4294967295,") | 481 | bytecode = bpf_bytecode("1,6 0 0 4294967295,") |
| @@ -549,6 +608,8 @@ try: | |||
| 549 | progs = bpftool_prog_list(expected=1) | 608 | progs = bpftool_prog_list(expected=1) |
| 550 | fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], | 609 | fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], |
| 551 | "Loaded program has wrong ID") | 610 | "Loaded program has wrong ID") |
| 611 | fail("dev" in progs[0].keys(), | ||
| 612 | "Device parameters reported for non-offloaded program") | ||
| 552 | 613 | ||
| 553 | start_test("Test XDP prog replace with bad flags...") | 614 | start_test("Test XDP prog replace with bad flags...") |
| 554 | ret, _ = sim.set_xdp(obj, "offload", force=True, fail=False) | 615 | ret, _ = sim.set_xdp(obj, "offload", force=True, fail=False) |
| @@ -673,6 +734,35 @@ try: | |||
| 673 | fail(time_diff < delay_sec, "Removal process took %s, expected %s" % | 734 | fail(time_diff < delay_sec, "Removal process took %s, expected %s" % |
| 674 | (time_diff, delay_sec)) | 735 | (time_diff, delay_sec)) |
| 675 | 736 | ||
| 737 | # Remove all pinned files and reinstantiate the netdev | ||
| 738 | clean_up() | ||
| 739 | bpftool_prog_list_wait(expected=0) | ||
| 740 | |||
| 741 | sim = NetdevSim() | ||
| 742 | sim.set_ethtool_tc_offloads(True) | ||
| 743 | sim.set_xdp(obj, "offload") | ||
| 744 | |||
| 745 | start_test("Test bpftool bound info reporting (own ns)...") | ||
| 746 | check_dev_info(False, "") | ||
| 747 | |||
| 748 | start_test("Test bpftool bound info reporting (other ns)...") | ||
| 749 | ns = mknetns() | ||
| 750 | sim.set_ns(ns) | ||
| 751 | check_dev_info(True, "") | ||
| 752 | |||
| 753 | start_test("Test bpftool bound info reporting (remote ns)...") | ||
| 754 | check_dev_info(False, ns) | ||
| 755 | |||
| 756 | start_test("Test bpftool bound info reporting (back to own ns)...") | ||
| 757 | sim.set_ns("") | ||
| 758 | check_dev_info(False, "") | ||
| 759 | |||
| 760 | pin_file, _ = pin_prog("/sys/fs/bpf/tmp") | ||
| 761 | sim.remove() | ||
| 762 | |||
| 763 | start_test("Test bpftool bound info reporting (removed dev)...") | ||
| 764 | check_dev_info(True, "", pin_file=pin_file, removed=True) | ||
| 765 | |||
| 676 | print("%s: OK" % (os.path.basename(__file__))) | 766 | print("%s: OK" % (os.path.basename(__file__))) |
| 677 | 767 | ||
| 678 | finally: | 768 | finally: |
