Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
[linux-2.6-microblaze.git] / kernel / bpf / syscall.c
index b41c6cf..8339d81 100644 (file)
@@ -103,6 +103,7 @@ int bpf_check_uarg_tail_zero(void __user *uaddr,
 const struct bpf_map_ops bpf_map_offload_ops = {
        .map_alloc = bpf_map_offload_map_alloc,
        .map_free = bpf_map_offload_map_free,
+       .map_check_btf = map_check_no_btf,
 };
 
 static struct bpf_map *find_and_alloc_map(union bpf_attr *attr)
@@ -181,32 +182,60 @@ int bpf_map_precharge_memlock(u32 pages)
        return 0;
 }
 
-static int bpf_map_charge_memlock(struct bpf_map *map)
+static int bpf_charge_memlock(struct user_struct *user, u32 pages)
 {
-       struct user_struct *user = get_current_user();
-       unsigned long memlock_limit;
+       unsigned long memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
 
-       memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+       if (atomic_long_add_return(pages, &user->locked_vm) > memlock_limit) {
+               atomic_long_sub(pages, &user->locked_vm);
+               return -EPERM;
+       }
+       return 0;
+}
 
-       atomic_long_add(map->pages, &user->locked_vm);
+static void bpf_uncharge_memlock(struct user_struct *user, u32 pages)
+{
+       atomic_long_sub(pages, &user->locked_vm);
+}
 
-       if (atomic_long_read(&user->locked_vm) > memlock_limit) {
-               atomic_long_sub(map->pages, &user->locked_vm);
+static int bpf_map_init_memlock(struct bpf_map *map)
+{
+       struct user_struct *user = get_current_user();
+       int ret;
+
+       ret = bpf_charge_memlock(user, map->pages);
+       if (ret) {
                free_uid(user);
-               return -EPERM;
+               return ret;
        }
        map->user = user;
-       return 0;
+       return ret;
 }
 
-static void bpf_map_uncharge_memlock(struct bpf_map *map)
+static void bpf_map_release_memlock(struct bpf_map *map)
 {
        struct user_struct *user = map->user;
-
-       atomic_long_sub(map->pages, &user->locked_vm);
+       bpf_uncharge_memlock(user, map->pages);
        free_uid(user);
 }
 
+int bpf_map_charge_memlock(struct bpf_map *map, u32 pages)
+{
+       int ret;
+
+       ret = bpf_charge_memlock(map->user, pages);
+       if (ret)
+               return ret;
+       map->pages += pages;
+       return ret;
+}
+
+void bpf_map_uncharge_memlock(struct bpf_map *map, u32 pages)
+{
+       bpf_uncharge_memlock(map->user, pages);
+       map->pages -= pages;
+}
+
 static int bpf_map_alloc_id(struct bpf_map *map)
 {
        int id;
@@ -256,7 +285,7 @@ static void bpf_map_free_deferred(struct work_struct *work)
 {
        struct bpf_map *map = container_of(work, struct bpf_map, work);
 
-       bpf_map_uncharge_memlock(map);
+       bpf_map_release_memlock(map);
        security_bpf_map_free(map);
        /* implementation dependent freeing */
        map->ops->map_free(map);
@@ -427,6 +456,34 @@ static int bpf_obj_name_cpy(char *dst, const char *src)
        return 0;
 }
 
+int map_check_no_btf(const struct bpf_map *map,
+                    const struct btf_type *key_type,
+                    const struct btf_type *value_type)
+{
+       return -ENOTSUPP;
+}
+
+static int map_check_btf(const struct bpf_map *map, const struct btf *btf,
+                        u32 btf_key_id, u32 btf_value_id)
+{
+       const struct btf_type *key_type, *value_type;
+       u32 key_size, value_size;
+       int ret = 0;
+
+       key_type = btf_type_id_size(btf, &btf_key_id, &key_size);
+       if (!key_type || key_size != map->key_size)
+               return -EINVAL;
+
+       value_type = btf_type_id_size(btf, &btf_value_id, &value_size);
+       if (!value_type || value_size != map->value_size)
+               return -EINVAL;
+
+       if (map->ops->map_check_btf)
+               ret = map->ops->map_check_btf(map, key_type, value_type);
+
+       return ret;
+}
+
 #define BPF_MAP_CREATE_LAST_FIELD btf_value_type_id
 /* called via syscall */
 static int map_create(union bpf_attr *attr)
@@ -461,8 +518,7 @@ static int map_create(union bpf_attr *attr)
        atomic_set(&map->refcnt, 1);
        atomic_set(&map->usercnt, 1);
 
-       if (bpf_map_support_seq_show(map) &&
-           (attr->btf_key_type_id || attr->btf_value_type_id)) {
+       if (attr->btf_key_type_id || attr->btf_value_type_id) {
                struct btf *btf;
 
                if (!attr->btf_key_type_id || !attr->btf_value_type_id) {
@@ -476,8 +532,8 @@ static int map_create(union bpf_attr *attr)
                        goto free_map_nouncharge;
                }
 
-               err = map->ops->map_check_btf(map, btf, attr->btf_key_type_id,
-                                             attr->btf_value_type_id);
+               err = map_check_btf(map, btf, attr->btf_key_type_id,
+                                   attr->btf_value_type_id);
                if (err) {
                        btf_put(btf);
                        goto free_map_nouncharge;
@@ -492,7 +548,7 @@ static int map_create(union bpf_attr *attr)
        if (err)
                goto free_map_nouncharge;
 
-       err = bpf_map_charge_memlock(map);
+       err = bpf_map_init_memlock(map);
        if (err)
                goto free_map_sec;
 
@@ -515,7 +571,7 @@ static int map_create(union bpf_attr *attr)
        return err;
 
 free_map:
-       bpf_map_uncharge_memlock(map);
+       bpf_map_release_memlock(map);
 free_map_sec:
        security_bpf_map_free(map);
 free_map_nouncharge:
@@ -656,6 +712,8 @@ static int map_lookup_elem(union bpf_attr *attr)
                err = bpf_fd_array_map_lookup_elem(map, key, value);
        } else if (IS_FD_HASH(map)) {
                err = bpf_fd_htab_map_lookup_elem(map, key, value);
+       } else if (map->map_type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY) {
+               err = bpf_fd_reuseport_array_lookup_elem(map, key, value);
        } else {
                rcu_read_lock();
                ptr = map->ops->map_lookup_elem(map, key);
@@ -762,6 +820,10 @@ static int map_update_elem(union bpf_attr *attr)
                err = bpf_fd_htab_map_update_elem(map, f.file, key, value,
                                                  attr->flags);
                rcu_read_unlock();
+       } else if (map->map_type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY) {
+               /* rcu_read_lock() is not needed */
+               err = bpf_fd_reuseport_array_update_elem(map, key, value,
+                                                        attr->flags);
        } else {
                rcu_read_lock();
                err = map->ops->map_update_elem(map, key, value, attr->flags);
@@ -929,6 +991,9 @@ static void free_used_maps(struct bpf_prog_aux *aux)
 {
        int i;
 
+       if (aux->cgroup_storage)
+               bpf_cgroup_storage_release(aux->prog, aux->cgroup_storage);
+
        for (i = 0; i < aux->used_map_cnt; i++)
                bpf_map_put(aux->used_maps[i]);