From: YiFei Zhu <zhuyifei@xxxxxxxxxx> This syscall attaches a map to a program. -EEXIST if the map is already attached to the program. call_rcu is used to free the old used_maps struct after an RCU grace period. Signed-off-by: YiFei Zhu <zhuyifei@xxxxxxxxxx> --- include/uapi/linux/bpf.h | 7 +++ kernel/bpf/syscall.c | 83 ++++++++++++++++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 7 +++ 3 files changed, 97 insertions(+) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index b134e679e9db..01b32036a0f5 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -118,6 +118,7 @@ enum bpf_cmd { BPF_ENABLE_STATS, BPF_ITER_CREATE, BPF_LINK_DETACH, + BPF_PROG_ADD_MAP, }; enum bpf_map_type { @@ -648,6 +649,12 @@ union bpf_attr { __u32 flags; } iter_create; + struct { /* struct used by BPF_PROG_ADD_MAP command */ + __u32 prog_fd; + __u32 map_fd; + __u32 flags; /* extra flags */ + } prog_add_map; + } __attribute__((aligned(8))); /* The description below is an attempt at providing documentation to eBPF diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 3fde9dc4b595..0564a4291184 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -4144,6 +4144,86 @@ static int bpf_iter_create(union bpf_attr *attr) return err; } +#define BPF_PROG_ADD_MAP_LAST_FIELD prog_add_map.flags + +static void __bpf_free_used_maps_rcu(struct rcu_head *rcu) +{ + struct bpf_used_maps *used_maps = container_of(rcu, struct bpf_used_maps, rcu); + + kfree(used_maps->arr); + kfree(used_maps); +} + +static int bpf_prog_add_map(union bpf_attr *attr) +{ + struct bpf_prog *prog; + struct bpf_map *map; + struct bpf_used_maps *used_maps_old, *used_maps_new; + int i, ret = 0; + + if (CHECK_ATTR(BPF_PROG_ADD_MAP)) + return -EINVAL; + + if (attr->prog_add_map.flags) + return -EINVAL; + + prog = bpf_prog_get(attr->prog_add_map.prog_fd); + if (IS_ERR(prog)) + return PTR_ERR(prog); + + map = bpf_map_get(attr->prog_add_map.map_fd); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto out_prog_put; + } + + used_maps_new = kzalloc(sizeof(*used_maps_new), GFP_KERNEL); + if (!used_maps_new) { + ret = -ENOMEM; + goto out_map_put; + } + + mutex_lock(&prog->aux->used_maps_mutex); + + used_maps_old = rcu_dereference_protected(prog->aux->used_maps, + lockdep_is_held(&prog->aux->used_maps_mutex)); + + for (i = 0; i < used_maps_old->cnt; i++) + if (used_maps_old->arr[i] == map) { + ret = -EEXIST; + goto out_unlock; + } + + used_maps_new->cnt = used_maps_old->cnt + 1; + used_maps_new->arr = kmalloc_array(used_maps_new->cnt, + sizeof(used_maps_new->arr[0]), + GFP_KERNEL); + if (!used_maps_new->arr) { + ret = -ENOMEM; + goto out_unlock; + } + + memcpy(used_maps_new->arr, used_maps_old->arr, + sizeof(used_maps_old->arr[0]) * used_maps_old->cnt); + used_maps_new->arr[used_maps_old->cnt] = map; + + rcu_assign_pointer(prog->aux->used_maps, used_maps_new); + call_rcu(&used_maps_old->rcu, __bpf_free_used_maps_rcu); + +out_unlock: + mutex_unlock(&prog->aux->used_maps_mutex); + + if (ret) + kfree(used_maps_new); + +out_map_put: + if (ret) + bpf_map_put(map); +out_prog_put: + bpf_prog_put(prog); + return ret; +} + SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) { union bpf_attr attr; @@ -4277,6 +4357,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz case BPF_LINK_DETACH: err = link_detach(&attr); break; + case BPF_PROG_ADD_MAP: + err = bpf_prog_add_map(&attr); + break; default: err = -EINVAL; break; diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index b134e679e9db..01b32036a0f5 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -118,6 +118,7 @@ enum bpf_cmd { BPF_ENABLE_STATS, BPF_ITER_CREATE, BPF_LINK_DETACH, + BPF_PROG_ADD_MAP, }; enum bpf_map_type { @@ -648,6 +649,12 @@ union bpf_attr { __u32 flags; } iter_create; + struct { /* struct used by BPF_PROG_ADD_MAP command */ + __u32 prog_fd; + __u32 map_fd; + __u32 flags; /* extra flags */ + } prog_add_map; + } __attribute__((aligned(8))); /* The description below is an attempt at providing documentation to eBPF -- 2.28.0