Commit 5620e1a8 authored by Daniel Borkmann's avatar Daniel Borkmann

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: default avatarDaniel Borkmann <daniel@iogearbox.net>
parents fb982666 752d7b45
...@@ -238,7 +238,7 @@ struct nfp_bpf_vnic { ...@@ -238,7 +238,7 @@ struct nfp_bpf_vnic {
int nfp_bpf_jit(struct nfp_prog *prog); int nfp_bpf_jit(struct nfp_prog *prog);
extern const struct bpf_ext_analyzer_ops nfp_bpf_analyzer_ops; extern const struct bpf_prog_offload_ops nfp_bpf_analyzer_ops;
struct netdev_bpf; struct netdev_bpf;
struct nfp_app; struct nfp_app;
......
...@@ -260,6 +260,6 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx) ...@@ -260,6 +260,6 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
return 0; return 0;
} }
const struct bpf_ext_analyzer_ops nfp_bpf_analyzer_ops = { const struct bpf_prog_offload_ops nfp_bpf_analyzer_ops = {
.insn_hook = nfp_verify_insn, .insn_hook = nfp_verify_insn,
}; };
...@@ -66,7 +66,7 @@ nsim_bpf_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn) ...@@ -66,7 +66,7 @@ nsim_bpf_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn)
return 0; return 0;
} }
static const struct bpf_ext_analyzer_ops nsim_bpf_analyzer_ops = { static const struct bpf_prog_offload_ops nsim_bpf_analyzer_ops = {
.insn_hook = nsim_bpf_verify_insn, .insn_hook = nsim_bpf_verify_insn,
}; };
......
...@@ -103,14 +103,14 @@ static void *__ns_get_path(struct path *path, struct ns_common *ns) ...@@ -103,14 +103,14 @@ static void *__ns_get_path(struct path *path, struct ns_common *ns)
goto got_it; goto got_it;
} }
void *ns_get_path(struct path *path, struct task_struct *task, void *ns_get_path_cb(struct path *path, ns_get_path_helper_t *ns_get_cb,
const struct proc_ns_operations *ns_ops) void *private_data)
{ {
struct ns_common *ns; struct ns_common *ns;
void *ret; void *ret;
again: again:
ns = ns_ops->get(task); ns = ns_get_cb(private_data);
if (!ns) if (!ns)
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
...@@ -120,6 +120,29 @@ void *ns_get_path(struct path *path, struct task_struct *task, ...@@ -120,6 +120,29 @@ void *ns_get_path(struct path *path, struct task_struct *task,
return ret; return ret;
} }
struct ns_get_path_task_args {
const struct proc_ns_operations *ns_ops;
struct task_struct *task;
};
static struct ns_common *ns_get_path_task(void *private_data)
{
struct ns_get_path_task_args *args = private_data;
return args->ns_ops->get(args->task);
}
void *ns_get_path(struct path *path, struct task_struct *task,
const struct proc_ns_operations *ns_ops)
{
struct ns_get_path_task_args args = {
.ns_ops = ns_ops,
.task = task,
};
return ns_get_path_cb(path, ns_get_path_task, &args);
}
int open_related_ns(struct ns_common *ns, int open_related_ns(struct ns_common *ns,
struct ns_common *(*get_ns)(struct ns_common *ns)) struct ns_common *(*get_ns)(struct ns_common *ns))
{ {
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/numa.h> #include <linux/numa.h>
#include <linux/wait.h> #include <linux/wait.h>
struct bpf_verifier_env;
struct perf_event; struct perf_event;
struct bpf_prog; struct bpf_prog;
struct bpf_map; struct bpf_map;
...@@ -184,14 +185,18 @@ struct bpf_verifier_ops { ...@@ -184,14 +185,18 @@ struct bpf_verifier_ops {
struct bpf_prog *prog, u32 *target_size); struct bpf_prog *prog, u32 *target_size);
}; };
struct bpf_prog_offload_ops {
int (*insn_hook)(struct bpf_verifier_env *env,
int insn_idx, int prev_insn_idx);
};
struct bpf_dev_offload { struct bpf_dev_offload {
struct bpf_prog *prog; struct bpf_prog *prog;
struct net_device *netdev; struct net_device *netdev;
void *dev_priv; void *dev_priv;
struct list_head offloads; struct list_head offloads;
bool dev_state; bool dev_state;
bool verifier_running; const struct bpf_prog_offload_ops *dev_ops;
wait_queue_head_t verifier_done;
}; };
struct bpf_prog_aux { struct bpf_prog_aux {
...@@ -201,6 +206,7 @@ struct bpf_prog_aux { ...@@ -201,6 +206,7 @@ struct bpf_prog_aux {
u32 stack_depth; u32 stack_depth;
u32 id; u32 id;
u32 func_cnt; u32 func_cnt;
bool offload_requested;
struct bpf_prog **func; struct bpf_prog **func;
void *jit_data; /* JIT specific data. arch dependent */ void *jit_data; /* JIT specific data. arch dependent */
struct latch_tree_node ksym_tnode; struct latch_tree_node ksym_tnode;
...@@ -351,6 +357,8 @@ void bpf_prog_put(struct bpf_prog *prog); ...@@ -351,6 +357,8 @@ void bpf_prog_put(struct bpf_prog *prog);
int __bpf_prog_charge(struct user_struct *user, u32 pages); int __bpf_prog_charge(struct user_struct *user, u32 pages);
void __bpf_prog_uncharge(struct user_struct *user, u32 pages); void __bpf_prog_uncharge(struct user_struct *user, u32 pages);
void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock);
struct bpf_map *bpf_map_get_with_uref(u32 ufd); struct bpf_map *bpf_map_get_with_uref(u32 ufd);
struct bpf_map *__bpf_map_get(struct fd f); struct bpf_map *__bpf_map_get(struct fd f);
struct bpf_map * __must_check bpf_map_inc(struct bpf_map *map, bool uref); 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,13 +531,15 @@ static inline struct bpf_prog *bpf_prog_get_type(u32 ufd,
int bpf_prog_offload_compile(struct bpf_prog *prog); int bpf_prog_offload_compile(struct bpf_prog *prog);
void bpf_prog_offload_destroy(struct bpf_prog *prog); void bpf_prog_offload_destroy(struct bpf_prog *prog);
int bpf_prog_offload_info_fill(struct bpf_prog_info *info,
struct bpf_prog *prog);
#if defined(CONFIG_NET) && defined(CONFIG_BPF_SYSCALL) #if defined(CONFIG_NET) && defined(CONFIG_BPF_SYSCALL)
int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr); int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr);
static inline bool bpf_prog_is_dev_bound(struct bpf_prog_aux *aux) static inline bool bpf_prog_is_dev_bound(struct bpf_prog_aux *aux)
{ {
return aux->offload; return aux->offload_requested;
} }
#else #else
static inline int bpf_prog_offload_init(struct bpf_prog *prog, static inline int bpf_prog_offload_init(struct bpf_prog *prog,
......
...@@ -166,12 +166,6 @@ static inline bool bpf_verifier_log_full(const struct bpf_verifer_log *log) ...@@ -166,12 +166,6 @@ static inline bool bpf_verifier_log_full(const struct bpf_verifer_log *log)
return log->len_used >= log->len_total - 1; return log->len_used >= log->len_total - 1;
} }
struct bpf_verifier_env;
struct bpf_ext_analyzer_ops {
int (*insn_hook)(struct bpf_verifier_env *env,
int insn_idx, int prev_insn_idx);
};
#define BPF_MAX_SUBPROGS 256 #define BPF_MAX_SUBPROGS 256
/* single container for all structs /* single container for all structs
...@@ -185,7 +179,6 @@ struct bpf_verifier_env { ...@@ -185,7 +179,6 @@ struct bpf_verifier_env {
bool strict_alignment; /* perform strict pointer alignment checks */ bool strict_alignment; /* perform strict pointer alignment checks */
struct bpf_verifier_state *cur_state; /* current verifier state */ struct bpf_verifier_state *cur_state; /* current verifier state */
struct bpf_verifier_state_list **explored_states; /* search pruning optimization */ struct bpf_verifier_state_list **explored_states; /* search pruning optimization */
const struct bpf_ext_analyzer_ops *dev_ops; /* device analyzer ops */
struct bpf_map *used_maps[MAX_USED_MAPS]; /* array of map's used by eBPF program */ struct bpf_map *used_maps[MAX_USED_MAPS]; /* array of map's used by eBPF program */
u32 used_map_cnt; /* number of used maps */ u32 used_map_cnt; /* number of used maps */
u32 id_gen; /* used to generate unique reg IDs */ 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,13 +199,8 @@ static inline struct bpf_reg_state *cur_regs(struct bpf_verifier_env *env)
return cur->frame[cur->curframe]->regs; return cur->frame[cur->curframe]->regs;
} }
#if defined(CONFIG_NET) && defined(CONFIG_BPF_SYSCALL)
int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env); int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env);
#else int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env,
static inline int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env) int insn_idx, int prev_insn_idx);
{
return -EOPNOTSUPP;
}
#endif
#endif /* _LINUX_BPF_VERIFIER_H */ #endif /* _LINUX_BPF_VERIFIER_H */
...@@ -804,7 +804,7 @@ enum bpf_netdev_command { ...@@ -804,7 +804,7 @@ enum bpf_netdev_command {
BPF_OFFLOAD_DESTROY, BPF_OFFLOAD_DESTROY,
}; };
struct bpf_ext_analyzer_ops; struct bpf_prog_offload_ops;
struct netlink_ext_ack; struct netlink_ext_ack;
struct netdev_bpf { struct netdev_bpf {
...@@ -826,7 +826,7 @@ struct netdev_bpf { ...@@ -826,7 +826,7 @@ struct netdev_bpf {
/* BPF_OFFLOAD_VERIFIER_PREP */ /* BPF_OFFLOAD_VERIFIER_PREP */
struct { struct {
struct bpf_prog *prog; struct bpf_prog *prog;
const struct bpf_ext_analyzer_ops *ops; /* callee set */ const struct bpf_prog_offload_ops *ops; /* callee set */
} verifier; } verifier;
/* BPF_OFFLOAD_TRANSLATE, BPF_OFFLOAD_DESTROY */ /* BPF_OFFLOAD_TRANSLATE, BPF_OFFLOAD_DESTROY */
struct { struct {
......
...@@ -78,6 +78,9 @@ extern struct file *proc_ns_fget(int fd); ...@@ -78,6 +78,9 @@ extern struct file *proc_ns_fget(int fd);
#define get_proc_ns(inode) ((struct ns_common *)(inode)->i_private) #define get_proc_ns(inode) ((struct ns_common *)(inode)->i_private)
extern void *ns_get_path(struct path *path, struct task_struct *task, extern void *ns_get_path(struct path *path, struct task_struct *task,
const struct proc_ns_operations *ns_ops); const struct proc_ns_operations *ns_ops);
typedef struct ns_common *ns_get_path_helper_t(void *);
extern void *ns_get_path_cb(struct path *path, ns_get_path_helper_t ns_get_cb,
void *private_data);
extern int ns_get_name(char *buf, size_t size, struct task_struct *task, extern int ns_get_name(char *buf, size_t size, struct task_struct *task,
const struct proc_ns_operations *ns_ops); const struct proc_ns_operations *ns_ops);
......
...@@ -921,6 +921,9 @@ struct bpf_prog_info { ...@@ -921,6 +921,9 @@ struct bpf_prog_info {
__u32 nr_map_ids; __u32 nr_map_ids;
__aligned_u64 map_ids; __aligned_u64 map_ids;
char name[BPF_OBJ_NAME_LEN]; char name[BPF_OBJ_NAME_LEN];
__u32 ifindex;
__u64 netns_dev;
__u64 netns_ino;
} __attribute__((aligned(8))); } __attribute__((aligned(8)));
struct bpf_map_info { struct bpf_map_info {
......
...@@ -16,17 +16,22 @@ ...@@ -16,17 +16,22 @@
#include <linux/bpf.h> #include <linux/bpf.h>
#include <linux/bpf_verifier.h> #include <linux/bpf_verifier.h>
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/kdev_t.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/printk.h> #include <linux/printk.h>
#include <linux/proc_ns.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/rwsem.h>
/* protected by RTNL */ /* Protects bpf_prog_offload_devs and offload members of all progs.
* RTNL lock cannot be taken when holding this lock.
*/
static DECLARE_RWSEM(bpf_devs_lock);
static LIST_HEAD(bpf_prog_offload_devs); static LIST_HEAD(bpf_prog_offload_devs);
int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr) 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; struct bpf_dev_offload *offload;
if (attr->prog_type != BPF_PROG_TYPE_SCHED_CLS && 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,32 +46,40 @@ int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr)
return -ENOMEM; return -ENOMEM;
offload->prog = prog; offload->prog = prog;
init_waitqueue_head(&offload->verifier_done);
rtnl_lock(); offload->netdev = dev_get_by_index(current->nsproxy->net_ns,
offload->netdev = __dev_get_by_index(net, attr->prog_ifindex); attr->prog_ifindex);
if (!offload->netdev) { if (!offload->netdev)
rtnl_unlock(); goto err_free;
kfree(offload);
return -EINVAL;
}
down_write(&bpf_devs_lock);
if (offload->netdev->reg_state != NETREG_REGISTERED)
goto err_unlock;
prog->aux->offload = offload; prog->aux->offload = offload;
list_add_tail(&offload->offloads, &bpf_prog_offload_devs); list_add_tail(&offload->offloads, &bpf_prog_offload_devs);
rtnl_unlock(); dev_put(offload->netdev);
up_write(&bpf_devs_lock);
return 0; return 0;
err_unlock:
up_write(&bpf_devs_lock);
dev_put(offload->netdev);
err_free:
kfree(offload);
return -EINVAL;
} }
static int __bpf_offload_ndo(struct bpf_prog *prog, enum bpf_netdev_command cmd, static int __bpf_offload_ndo(struct bpf_prog *prog, enum bpf_netdev_command cmd,
struct netdev_bpf *data) struct netdev_bpf *data)
{ {
struct net_device *netdev = prog->aux->offload->netdev; struct bpf_dev_offload *offload = prog->aux->offload;
struct net_device *netdev;
ASSERT_RTNL(); ASSERT_RTNL();
if (!netdev) if (!offload)
return -ENODEV; return -ENODEV;
netdev = offload->netdev;
if (!netdev->netdev_ops->ndo_bpf) if (!netdev->netdev_ops->ndo_bpf)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -87,62 +100,63 @@ int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env) ...@@ -87,62 +100,63 @@ int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env)
if (err) if (err)
goto exit_unlock; goto exit_unlock;
env->dev_ops = data.verifier.ops; env->prog->aux->offload->dev_ops = data.verifier.ops;
env->prog->aux->offload->dev_state = true; env->prog->aux->offload->dev_state = true;
env->prog->aux->offload->verifier_running = true;
exit_unlock: exit_unlock:
rtnl_unlock(); rtnl_unlock();
return err; return err;
} }
int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env,
int insn_idx, int prev_insn_idx)
{
struct bpf_dev_offload *offload;
int ret = -ENODEV;
down_read(&bpf_devs_lock);
offload = env->prog->aux->offload;
if (offload)
ret = offload->dev_ops->insn_hook(env, insn_idx, prev_insn_idx);
up_read(&bpf_devs_lock);
return ret;
}
static void __bpf_prog_offload_destroy(struct bpf_prog *prog) static void __bpf_prog_offload_destroy(struct bpf_prog *prog)
{ {
struct bpf_dev_offload *offload = prog->aux->offload; struct bpf_dev_offload *offload = prog->aux->offload;
struct netdev_bpf data = {}; struct netdev_bpf data = {};
/* Caution - if netdev is destroyed before the program, this function
* will be called twice.
*/
data.offload.prog = prog; data.offload.prog = prog;
if (offload->verifier_running)
wait_event(offload->verifier_done, !offload->verifier_running);
if (offload->dev_state) if (offload->dev_state)
WARN_ON(__bpf_offload_ndo(prog, BPF_OFFLOAD_DESTROY, &data)); WARN_ON(__bpf_offload_ndo(prog, BPF_OFFLOAD_DESTROY, &data));
offload->dev_state = false; /* Make sure BPF_PROG_GET_NEXT_ID can't find this dead program */
bpf_prog_free_id(prog, true);
list_del_init(&offload->offloads); list_del_init(&offload->offloads);
offload->netdev = NULL; kfree(offload);
prog->aux->offload = NULL;
} }
void bpf_prog_offload_destroy(struct bpf_prog *prog) 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(); rtnl_lock();
__bpf_prog_offload_destroy(prog); down_write(&bpf_devs_lock);
if (prog->aux->offload)
__bpf_prog_offload_destroy(prog);
up_write(&bpf_devs_lock);
rtnl_unlock(); rtnl_unlock();
kfree(offload);
} }
static int bpf_prog_offload_translate(struct bpf_prog *prog) static int bpf_prog_offload_translate(struct bpf_prog *prog)
{ {
struct bpf_dev_offload *offload = prog->aux->offload;
struct netdev_bpf data = {}; struct netdev_bpf data = {};
int ret; int ret;
data.offload.prog = prog; data.offload.prog = prog;
offload->verifier_running = false;
wake_up(&offload->verifier_done);
rtnl_lock(); rtnl_lock();
ret = __bpf_offload_ndo(prog, BPF_OFFLOAD_TRANSLATE, &data); ret = __bpf_offload_ndo(prog, BPF_OFFLOAD_TRANSLATE, &data);
rtnl_unlock(); rtnl_unlock();
...@@ -164,6 +178,63 @@ int bpf_prog_offload_compile(struct bpf_prog *prog) ...@@ -164,6 +178,63 @@ int bpf_prog_offload_compile(struct bpf_prog *prog)
return bpf_prog_offload_translate(prog); return bpf_prog_offload_translate(prog);
} }
struct ns_get_path_bpf_prog_args {
struct bpf_prog *prog;
struct bpf_prog_info *info;
};
static struct ns_common *bpf_prog_offload_info_fill_ns(void *private_data)
{
struct ns_get_path_bpf_prog_args *args = private_data;
struct bpf_prog_aux *aux = args->prog->aux;
struct ns_common *ns;
struct net *net;
rtnl_lock();
down_read(&bpf_devs_lock);
if (aux->offload) {
args->info->ifindex = aux->offload->netdev->ifindex;
net = dev_net(aux->offload->netdev);
get_net(net);
ns = &net->ns;
} else {
args->info->ifindex = 0;
ns = NULL;
}
up_read(&bpf_devs_lock);
rtnl_unlock();
return ns;
}
int bpf_prog_offload_info_fill(struct bpf_prog_info *info,
struct bpf_prog *prog)
{
struct ns_get_path_bpf_prog_args args = {
.prog = prog,
.info = info,
};
struct inode *ns_inode;
struct path ns_path;
void *res;
res = ns_get_path_cb(&ns_path, bpf_prog_offload_info_fill_ns, &args);
if (IS_ERR(res)) {
if (!info->ifindex)
return -ENODEV;
return PTR_ERR(res);
}
ns_inode = ns_path.dentry->d_inode;
info->netns_dev = new_encode_dev(ns_inode->i_sb->s_dev);
info->netns_ino = ns_inode->i_ino;
path_put(&ns_path);
return 0;
}
const struct bpf_prog_ops bpf_offload_prog_ops = { const struct bpf_prog_ops bpf_offload_prog_ops = {
}; };
...@@ -181,11 +252,13 @@ static int bpf_offload_notification(struct notifier_block *notifier, ...@@ -181,11 +252,13 @@ static int bpf_offload_notification(struct notifier_block *notifier,
if (netdev->reg_state != NETREG_UNREGISTERING) if (netdev->reg_state != NETREG_UNREGISTERING)
break; break;
down_write(&bpf_devs_lock);
list_for_each_entry_safe(offload, tmp, &bpf_prog_offload_devs, list_for_each_entry_safe(offload, tmp, &bpf_prog_offload_devs,
offloads) { offloads) {
if (offload->netdev == netdev) if (offload->netdev == netdev)
__bpf_prog_offload_destroy(offload->prog); __bpf_prog_offload_destroy(offload->prog);
} }
up_write(&bpf_devs_lock);
break; break;
default: default:
break; break;
......
...@@ -905,9 +905,13 @@ static int bpf_prog_alloc_id(struct bpf_prog *prog) ...@@ -905,9 +905,13 @@ static int bpf_prog_alloc_id(struct bpf_prog *prog)
return id > 0 ? 0 : id; return id > 0 ? 0 : id;
} }
static void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock) void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock)
{ {
/* cBPF to eBPF migrations are currently not in the idr store. */ /* cBPF to eBPF migrations are currently not in the idr store.
* Offloaded programs are removed from the store when their device
* disappears - even if someone grabs an fd to them they are unusable,
* simply waiting for refcnt to drop to be freed.
*/
if (!prog->aux->id) if (!prog->aux->id)
return; return;
...@@ -917,6 +921,7 @@ static void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock) ...@@ -917,6 +921,7 @@ static void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock)
__acquire(&prog_idr_lock); __acquire(&prog_idr_lock);
idr_remove(&prog_idr, prog->aux->id); idr_remove(&prog_idr, prog->aux->id);
prog->aux->id = 0;
if (do_idr_lock) if (do_idr_lock)
spin_unlock_bh(&prog_idr_lock); spin_unlock_bh(&prog_idr_lock);
...@@ -1157,6 +1162,8 @@ static int bpf_prog_load(union bpf_attr *attr) ...@@ -1157,6 +1162,8 @@ static int bpf_prog_load(union bpf_attr *attr)
if (!prog) if (!prog)
return -ENOMEM; return -ENOMEM;
prog->aux->offload_requested = !!attr->prog_ifindex;
err = security_bpf_prog_alloc(prog->aux); err = security_bpf_prog_alloc(prog->aux);
if (err) if (err)
goto free_prog_nouncharge; goto free_prog_nouncharge;
...@@ -1178,7 +1185,7 @@ static int bpf_prog_load(union bpf_attr *attr) ...@@ -1178,7 +1185,7 @@ static int bpf_prog_load(union bpf_attr *attr)
atomic_set(&prog->aux->refcnt, 1); atomic_set(&prog->aux->refcnt, 1);
prog->gpl_compatible = is_gpl ? 1 : 0; prog->gpl_compatible = is_gpl ? 1 : 0;
if (attr->prog_ifindex) { if (bpf_prog_is_dev_bound(prog->aux)) {
err = bpf_prog_offload_init(prog, attr); err = bpf_prog_offload_init(prog, attr);
if (err) if (err)
goto free_prog; goto free_prog;
...@@ -1700,6 +1707,12 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog, ...@@ -1700,6 +1707,12 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
return -EFAULT; return -EFAULT;
} }
if (bpf_prog_is_dev_bound(prog->aux)) {
err = bpf_prog_offload_info_fill(&info, prog);
if (err)
return err;
}
done: done:
if (copy_to_user(uinfo, &info, info_len) || if (copy_to_user(uinfo, &info, info_len) ||
put_user(info_len, &uattr->info.info_len)) put_user(info_len, &uattr->info.info_len))
......
...@@ -4438,15 +4438,6 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) ...@@ -4438,15 +4438,6 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
return 0; return 0;
} }
static int ext_analyzer_insn_hook(struct bpf_verifier_env *env,
int insn_idx, int prev_insn_idx)
{
if (env->dev_ops && env->dev_ops->insn_hook)
return env->dev_ops->insn_hook(env, insn_idx, prev_insn_idx);
return 0;
}
static int do_check(struct bpf_verifier_env *env) static int do_check(struct bpf_verifier_env *env)
{ {
struct bpf_verifier_state *state; struct bpf_verifier_state *state;
...@@ -4531,9 +4522,12 @@ static int do_check(struct bpf_verifier_env *env) ...@@ -4531,9 +4522,12 @@ static int do_check(struct bpf_verifier_env *env)
print_bpf_insn(&cbs, env, insn, env->allow_ptr_leaks); print_bpf_insn(&cbs, env, insn, env->allow_ptr_leaks);
} }
err = ext_analyzer_insn_hook(env, insn_idx, prev_insn_idx); if (bpf_prog_is_dev_bound(env->prog->aux)) {
if (err) err = bpf_prog_offload_verify_insn(env, insn_idx,
return err; prev_insn_idx);
if (err)
return err;
}
regs = cur_regs(env); regs = cur_regs(env);
env->insn_aux_data[insn_idx].seen = true; env->insn_aux_data[insn_idx].seen = true;
...@@ -5463,7 +5457,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) ...@@ -5463,7 +5457,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))
env->strict_alignment = true; env->strict_alignment = true;
if (env->prog->aux->offload) { if (bpf_prog_is_dev_bound(env->prog->aux)) {
ret = bpf_prog_offload_verifier_prep(env); ret = bpf_prog_offload_verifier_prep(env);
if (ret) if (ret)
goto err_unlock; goto err_unlock;
......
...@@ -44,7 +44,9 @@ ...@@ -44,7 +44,9 @@
#include <unistd.h> #include <unistd.h>
#include <linux/limits.h> #include <linux/limits.h>
#include <linux/magic.h> #include <linux/magic.h>
#include <net/if.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/vfs.h> #include <sys/vfs.h>
...@@ -412,3 +414,53 @@ void delete_pinned_obj_table(struct pinned_obj_table *tab) ...@@ -412,3 +414,53 @@ void delete_pinned_obj_table(struct pinned_obj_table *tab)
free(obj); free(obj);
} }
} }
static char *
ifindex_to_name_ns(__u32 ifindex, __u32 ns_dev, __u32 ns_ino, char *buf)
{
struct stat st;
int err;
err = stat("/proc/self/ns/net", &st);
if (err) {
p_err("Can't stat /proc/self: %s", strerror(errno));
return NULL;
}
if (st.st_dev != ns_dev || st.st_ino != ns_ino)
return NULL;
return if_indextoname(ifindex, buf);
}
void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
{
char name[IF_NAMESIZE];
if (!ifindex)
return;
printf(" dev ");
if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
printf("%s", name);
else
printf("ifindex %u ns_dev %llu ns_ino %llu",
ifindex, ns_dev, ns_inode);
}
void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
{
char name[IF_NAMESIZE];
if (!ifindex)
return;
jsonw_name(json_wtr, "dev");
jsonw_start_object(json_wtr);
jsonw_uint_field(json_wtr, "ifindex", ifindex);
jsonw_uint_field(json_wtr, "ns_dev", ns_dev);
jsonw_uint_field(json_wtr, "ns_inode", ns_inode);
if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
jsonw_string_field(json_wtr, "ifname", name);
jsonw_end_object(json_wtr);
}
...@@ -96,6 +96,8 @@ struct pinned_obj { ...@@ -96,6 +96,8 @@ struct pinned_obj {
int build_pinned_obj_table(struct pinned_obj_table *table, int build_pinned_obj_table(struct pinned_obj_table *table,
enum bpf_obj_type type); enum bpf_obj_type type);
void delete_pinned_obj_table(struct pinned_obj_table *tab); void delete_pinned_obj_table(struct pinned_obj_table *tab);
void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode);
void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode);
struct cmd { struct cmd {
const char *cmd; const char *cmd;
......
...@@ -230,6 +230,8 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) ...@@ -230,6 +230,8 @@ static void print_prog_json(struct bpf_prog_info *info, int fd)
info->tag[0], info->tag[1], info->tag[2], info->tag[3], info->tag[0], info->tag[1], info->tag[2], info->tag[3],
info->tag[4], info->tag[5], info->tag[6], info->tag[7]); info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
print_dev_json(info->ifindex, info->netns_dev, info->netns_ino);
if (info->load_time) { if (info->load_time) {
char buf[32]; char buf[32];
...@@ -287,6 +289,7 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd) ...@@ -287,6 +289,7 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd)
printf("tag "); printf("tag ");
fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino);
printf("\n"); printf("\n");
if (info->load_time) { if (info->load_time) {
......
...@@ -921,6 +921,9 @@ struct bpf_prog_info { ...@@ -921,6 +921,9 @@ struct bpf_prog_info {
__u32 nr_map_ids; __u32 nr_map_ids;
__aligned_u64 map_ids; __aligned_u64 map_ids;
char name[BPF_OBJ_NAME_LEN]; char name[BPF_OBJ_NAME_LEN];
__u32 ifindex;
__u64 netns_dev;
__u64 netns_ino;
} __attribute__((aligned(8))); } __attribute__((aligned(8)));
struct bpf_map_info { struct bpf_map_info {
......
...@@ -18,6 +18,8 @@ import argparse ...@@ -18,6 +18,8 @@ import argparse
import json import json
import os import os
import pprint import pprint
import random
import string
import subprocess import subprocess
import time import time
...@@ -27,6 +29,7 @@ bpf_test_dir = os.path.dirname(os.path.realpath(__file__)) ...@@ -27,6 +29,7 @@ bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
pp = pprint.PrettyPrinter() pp = pprint.PrettyPrinter()
devs = [] # devices we created for clean up devs = [] # devices we created for clean up
files = [] # files to be removed files = [] # files to be removed
netns = [] # net namespaces to be removed
def log_get_sec(level=0): def log_get_sec(level=0):
return "*" * (log_level + level) return "*" * (log_level + level)
...@@ -128,22 +131,25 @@ def rm(f): ...@@ -128,22 +131,25 @@ def rm(f):
if f in files: if f in files:
files.remove(f) files.remove(f)
def tool(name, args, flags, JSON=True, fail=True): def tool(name, args, flags, JSON=True, ns="", fail=True):
params = "" params = ""
if JSON: if JSON:
params += "%s " % (flags["json"]) params += "%s " % (flags["json"])
ret, out = cmd(name + " " + params + args, fail=fail) if ns != "":
ns = "ip netns exec %s " % (ns)
ret, out = cmd(ns + name + " " + params + args, fail=fail)
if JSON and len(out.strip()) != 0: if JSON and len(out.strip()) != 0:
return ret, json.loads(out) return ret, json.loads(out)
else: else:
return ret, out return ret, out
def bpftool(args, JSON=True, fail=True): def bpftool(args, JSON=True, ns="", fail=True):
return tool("bpftool", args, {"json":"-p"}, JSON=JSON, fail=fail) return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail)
def bpftool_prog_list(expected=None): def bpftool_prog_list(expected=None, ns=""):
_, progs = bpftool("prog show", JSON=True, fail=True) _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
if expected is not None: if expected is not None:
if len(progs) != expected: if len(progs) != expected:
fail(True, "%d BPF programs loaded, expected %d" % fail(True, "%d BPF programs loaded, expected %d" %
...@@ -158,13 +164,13 @@ def bpftool_prog_list_wait(expected=0, n_retry=20): ...@@ -158,13 +164,13 @@ def bpftool_prog_list_wait(expected=0, n_retry=20):
time.sleep(0.05) time.sleep(0.05)
raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs)) raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
def ip(args, force=False, JSON=True, fail=True): def ip(args, force=False, JSON=True, ns="", fail=True):
if force: if force:
args = "-force " + args args = "-force " + args
return tool("ip", args, {"json":"-j"}, JSON=JSON, fail=fail) return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns, fail=fail)
def tc(args, JSON=True, fail=True): def tc(args, JSON=True, ns="", fail=True):
return tool("tc", args, {"json":"-p"}, JSON=JSON, fail=fail) return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail)
def ethtool(dev, opt, args, fail=True): def ethtool(dev, opt, args, fail=True):
return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail) return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
...@@ -178,6 +184,15 @@ def bpf_pinned(name): ...@@ -178,6 +184,15 @@ def bpf_pinned(name):
def bpf_bytecode(bytecode): def bpf_bytecode(bytecode):
return "bytecode \"%s\"" % (bytecode) return "bytecode \"%s\"" % (bytecode)
def mknetns(n_retry=10):
for i in range(n_retry):
name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
ret, _ = ip("netns add %s" % (name), fail=False)
if ret == 0:
netns.append(name)
return name
return None
class DebugfsDir: class DebugfsDir:
""" """
Class for accessing DebugFS directories as a dictionary. Class for accessing DebugFS directories as a dictionary.
...@@ -237,6 +252,8 @@ class NetdevSim: ...@@ -237,6 +252,8 @@ class NetdevSim:
self.dev = self._netdevsim_create() self.dev = self._netdevsim_create()
devs.append(self) devs.append(self)
self.ns = ""
self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname']) self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname'])
self.dfs_refresh() self.dfs_refresh()
...@@ -257,7 +274,7 @@ class NetdevSim: ...@@ -257,7 +274,7 @@ class NetdevSim:
def remove(self): def remove(self):
devs.remove(self) devs.remove(self)
ip("link del dev %s" % (self.dev["ifname"])) ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns)
def dfs_refresh(self): def dfs_refresh(self):
self.dfs = DebugfsDir(self.dfs_dir) self.dfs = DebugfsDir(self.dfs_dir)
...@@ -285,6 +302,11 @@ class NetdevSim: ...@@ -285,6 +302,11 @@ class NetdevSim:
time.sleep(0.05) time.sleep(0.05)
raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs)) raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
def set_ns(self, ns):
name = "1" if ns == "" else ns
ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
self.ns = ns
def set_mtu(self, mtu, fail=True): def set_mtu(self, mtu, fail=True):
return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu), return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
fail=fail) fail=fail)
...@@ -372,6 +394,8 @@ def clean_up(): ...@@ -372,6 +394,8 @@ def clean_up():
dev.remove() dev.remove()
for f in files: for f in files:
cmd("rm -f %s" % (f)) cmd("rm -f %s" % (f))
for ns in netns:
cmd("ip netns delete %s" % (ns))
def pin_prog(file_name, idx=0): def pin_prog(file_name, idx=0):
progs = bpftool_prog_list(expected=(idx + 1)) progs = bpftool_prog_list(expected=(idx + 1))
...@@ -381,6 +405,35 @@ def pin_prog(file_name, idx=0): ...@@ -381,6 +405,35 @@ def pin_prog(file_name, idx=0):
return file_name, bpf_pinned(file_name) return file_name, bpf_pinned(file_name)
def check_dev_info(other_ns, ns, pin_file=None, removed=False):
if removed:
bpftool_prog_list(expected=0)
ret, err = bpftool("prog show pin %s" % (pin_file), fail=False)
fail(ret == 0, "Showing prog with removed device did not fail")
fail(err["error"].find("No such device") == -1,
"Showing prog with removed device expected ENODEV, error is %s" %
(err["error"]))
return
progs = bpftool_prog_list(expected=int(not removed), ns=ns)
prog = progs[0]
fail("dev" not in prog.keys(), "Device parameters not reported")
dev = prog["dev"]
fail("ifindex" not in dev.keys(), "Device parameters not reported")
fail("ns_dev" not in dev.keys(), "Device parameters not reported")
fail("ns_inode" not in dev.keys(), "Device parameters not reported")
if not removed and not other_ns:
fail("ifname" not in dev.keys(), "Ifname not reported")
fail(dev["ifname"] != sim["ifname"],
"Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
else:
fail("ifname" in dev.keys(), "Ifname is reported for other ns")
if removed:
fail(dev["ifindex"] != 0, "Device perameters not zero on removed")
fail(dev["ns_dev"] != 0, "Device perameters not zero on removed")
fail(dev["ns_inode"] != 0, "Device perameters not zero on removed")
# Parse command line # Parse command line
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--log", help="output verbose log to given file") parser.add_argument("--log", help="output verbose log to given file")
...@@ -417,6 +470,12 @@ for s in samples: ...@@ -417,6 +470,12 @@ for s in samples:
skip(ret != 0, "sample %s/%s not found, please compile it" % skip(ret != 0, "sample %s/%s not found, please compile it" %
(bpf_test_dir, s)) (bpf_test_dir, s))
# Check if net namespaces seem to work
ns = mknetns()
skip(ns is None, "Could not create a net namespace")
cmd("ip netns delete %s" % (ns))
netns = []
try: try:
obj = bpf_obj("sample_ret0.o") obj = bpf_obj("sample_ret0.o")
bytecode = bpf_bytecode("1,6 0 0 4294967295,") bytecode = bpf_bytecode("1,6 0 0 4294967295,")
...@@ -549,6 +608,8 @@ try: ...@@ -549,6 +608,8 @@ try:
progs = bpftool_prog_list(expected=1) progs = bpftool_prog_list(expected=1)
fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
"Loaded program has wrong ID") "Loaded program has wrong ID")
fail("dev" in progs[0].keys(),
"Device parameters reported for non-offloaded program")
start_test("Test XDP prog replace with bad flags...") start_test("Test XDP prog replace with bad flags...")
ret, _ = sim.set_xdp(obj, "offload", force=True, fail=False) ret, _ = sim.set_xdp(obj, "offload", force=True, fail=False)
...@@ -673,6 +734,35 @@ try: ...@@ -673,6 +734,35 @@ try:
fail(time_diff < delay_sec, "Removal process took %s, expected %s" % fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
(time_diff, delay_sec)) (time_diff, delay_sec))
# Remove all pinned files and reinstantiate the netdev
clean_up()
bpftool_prog_list_wait(expected=0)
sim = NetdevSim()
sim.set_ethtool_tc_offloads(True)
sim.set_xdp(obj, "offload")
start_test("Test bpftool bound info reporting (own ns)...")
check_dev_info(False, "")
start_test("Test bpftool bound info reporting (other ns)...")
ns = mknetns()
sim.set_ns(ns)
check_dev_info(True, "")
start_test("Test bpftool bound info reporting (remote ns)...")
check_dev_info(False, ns)
start_test("Test bpftool bound info reporting (back to own ns)...")
sim.set_ns("")
check_dev_info(False, "")
pin_file, _ = pin_prog("/sys/fs/bpf/tmp")
sim.remove()
start_test("Test bpftool bound info reporting (removed dev)...")
check_dev_info(True, "", pin_file=pin_file, removed=True)
print("%s: OK" % (os.path.basename(__file__))) print("%s: OK" % (os.path.basename(__file__)))
finally: finally:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment