drivers: atm: nicstar: Use the correct function to free some resources
[cascardo/linux.git] / kernel / bpf / arraymap.c
index 76d5a79..633a650 100644 (file)
@@ -328,8 +328,8 @@ static void *fd_array_map_lookup_elem(struct bpf_map *map, void *key)
 }
 
 /* only called from syscall */
-static int fd_array_map_update_elem(struct bpf_map *map, void *key,
-                                   void *value, u64 map_flags)
+int bpf_fd_array_map_update_elem(struct bpf_map *map, struct file *map_file,
+                                void *key, void *value, u64 map_flags)
 {
        struct bpf_array *array = container_of(map, struct bpf_array, map);
        void *new_ptr, *old_ptr;
@@ -342,7 +342,7 @@ static int fd_array_map_update_elem(struct bpf_map *map, void *key,
                return -E2BIG;
 
        ufd = *(u32 *)value;
-       new_ptr = map->ops->map_fd_get_ptr(map, ufd);
+       new_ptr = map->ops->map_fd_get_ptr(map, map_file, ufd);
        if (IS_ERR(new_ptr))
                return PTR_ERR(new_ptr);
 
@@ -371,10 +371,12 @@ static int fd_array_map_delete_elem(struct bpf_map *map, void *key)
        }
 }
 
-static void *prog_fd_array_get_ptr(struct bpf_map *map, int fd)
+static void *prog_fd_array_get_ptr(struct bpf_map *map,
+                                  struct file *map_file, int fd)
 {
        struct bpf_array *array = container_of(map, struct bpf_array, map);
        struct bpf_prog *prog = bpf_prog_get(fd);
+
        if (IS_ERR(prog))
                return prog;
 
@@ -382,14 +384,13 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map, int fd)
                bpf_prog_put(prog);
                return ERR_PTR(-EINVAL);
        }
+
        return prog;
 }
 
 static void prog_fd_array_put_ptr(void *ptr)
 {
-       struct bpf_prog *prog = ptr;
-
-       bpf_prog_put_rcu(prog);
+       bpf_prog_put(ptr);
 }
 
 /* decrement refcnt of all bpf_progs that are stored in this map */
@@ -407,7 +408,6 @@ static const struct bpf_map_ops prog_array_ops = {
        .map_free = fd_array_map_free,
        .map_get_next_key = array_map_get_next_key,
        .map_lookup_elem = fd_array_map_lookup_elem,
-       .map_update_elem = fd_array_map_update_elem,
        .map_delete_elem = fd_array_map_delete_elem,
        .map_fd_get_ptr = prog_fd_array_get_ptr,
        .map_fd_put_ptr = prog_fd_array_put_ptr,
@@ -425,59 +425,105 @@ static int __init register_prog_array_map(void)
 }
 late_initcall(register_prog_array_map);
 
-static void perf_event_array_map_free(struct bpf_map *map)
+static struct bpf_event_entry *bpf_event_entry_gen(struct file *perf_file,
+                                                  struct file *map_file)
 {
-       bpf_fd_array_map_clear(map);
-       fd_array_map_free(map);
+       struct bpf_event_entry *ee;
+
+       ee = kzalloc(sizeof(*ee), GFP_ATOMIC);
+       if (ee) {
+               ee->event = perf_file->private_data;
+               ee->perf_file = perf_file;
+               ee->map_file = map_file;
+       }
+
+       return ee;
 }
 
-static void *perf_event_fd_array_get_ptr(struct bpf_map *map, int fd)
+static void __bpf_event_entry_free(struct rcu_head *rcu)
 {
-       struct perf_event *event;
-       const struct perf_event_attr *attr;
-       struct file *file;
+       struct bpf_event_entry *ee;
 
-       file = perf_event_get(fd);
-       if (IS_ERR(file))
-               return file;
+       ee = container_of(rcu, struct bpf_event_entry, rcu);
+       fput(ee->perf_file);
+       kfree(ee);
+}
 
-       event = file->private_data;
+static void bpf_event_entry_free_rcu(struct bpf_event_entry *ee)
+{
+       call_rcu(&ee->rcu, __bpf_event_entry_free);
+}
 
-       attr = perf_event_attrs(event);
-       if (IS_ERR(attr))
-               goto err;
+static void *perf_event_fd_array_get_ptr(struct bpf_map *map,
+                                        struct file *map_file, int fd)
+{
+       const struct perf_event_attr *attr;
+       struct bpf_event_entry *ee;
+       struct perf_event *event;
+       struct file *perf_file;
 
-       if (attr->inherit)
-               goto err;
+       perf_file = perf_event_get(fd);
+       if (IS_ERR(perf_file))
+               return perf_file;
 
-       if (attr->type == PERF_TYPE_RAW)
-               return file;
+       event = perf_file->private_data;
+       ee = ERR_PTR(-EINVAL);
 
-       if (attr->type == PERF_TYPE_HARDWARE)
-               return file;
+       attr = perf_event_attrs(event);
+       if (IS_ERR(attr) || attr->inherit)
+               goto err_out;
+
+       switch (attr->type) {
+       case PERF_TYPE_SOFTWARE:
+               if (attr->config != PERF_COUNT_SW_BPF_OUTPUT)
+                       goto err_out;
+               /* fall-through */
+       case PERF_TYPE_RAW:
+       case PERF_TYPE_HARDWARE:
+               ee = bpf_event_entry_gen(perf_file, map_file);
+               if (ee)
+                       return ee;
+               ee = ERR_PTR(-ENOMEM);
+               /* fall-through */
+       default:
+               break;
+       }
 
-       if (attr->type == PERF_TYPE_SOFTWARE &&
-           attr->config == PERF_COUNT_SW_BPF_OUTPUT)
-               return file;
-err:
-       fput(file);
-       return ERR_PTR(-EINVAL);
+err_out:
+       fput(perf_file);
+       return ee;
 }
 
 static void perf_event_fd_array_put_ptr(void *ptr)
 {
-       fput((struct file *)ptr);
+       bpf_event_entry_free_rcu(ptr);
+}
+
+static void perf_event_fd_array_release(struct bpf_map *map,
+                                       struct file *map_file)
+{
+       struct bpf_array *array = container_of(map, struct bpf_array, map);
+       struct bpf_event_entry *ee;
+       int i;
+
+       rcu_read_lock();
+       for (i = 0; i < array->map.max_entries; i++) {
+               ee = READ_ONCE(array->ptrs[i]);
+               if (ee && ee->map_file == map_file)
+                       fd_array_map_delete_elem(map, &i);
+       }
+       rcu_read_unlock();
 }
 
 static const struct bpf_map_ops perf_event_array_ops = {
        .map_alloc = fd_array_map_alloc,
-       .map_free = perf_event_array_map_free,
+       .map_free = fd_array_map_free,
        .map_get_next_key = array_map_get_next_key,
        .map_lookup_elem = fd_array_map_lookup_elem,
-       .map_update_elem = fd_array_map_update_elem,
        .map_delete_elem = fd_array_map_delete_elem,
        .map_fd_get_ptr = perf_event_fd_array_get_ptr,
        .map_fd_put_ptr = perf_event_fd_array_put_ptr,
+       .map_release = perf_event_fd_array_release,
 };
 
 static struct bpf_map_type_list perf_event_array_type __read_mostly = {
@@ -491,3 +537,46 @@ static int __init register_perf_event_array_map(void)
        return 0;
 }
 late_initcall(register_perf_event_array_map);
+
+#ifdef CONFIG_SOCK_CGROUP_DATA
+static void *cgroup_fd_array_get_ptr(struct bpf_map *map,
+                                    struct file *map_file /* not used */,
+                                    int fd)
+{
+       return cgroup_get_from_fd(fd);
+}
+
+static void cgroup_fd_array_put_ptr(void *ptr)
+{
+       /* cgroup_put free cgrp after a rcu grace period */
+       cgroup_put(ptr);
+}
+
+static void cgroup_fd_array_free(struct bpf_map *map)
+{
+       bpf_fd_array_map_clear(map);
+       fd_array_map_free(map);
+}
+
+static const struct bpf_map_ops cgroup_array_ops = {
+       .map_alloc = fd_array_map_alloc,
+       .map_free = cgroup_fd_array_free,
+       .map_get_next_key = array_map_get_next_key,
+       .map_lookup_elem = fd_array_map_lookup_elem,
+       .map_delete_elem = fd_array_map_delete_elem,
+       .map_fd_get_ptr = cgroup_fd_array_get_ptr,
+       .map_fd_put_ptr = cgroup_fd_array_put_ptr,
+};
+
+static struct bpf_map_type_list cgroup_array_type __read_mostly = {
+       .ops = &cgroup_array_ops,
+       .type = BPF_MAP_TYPE_CGROUP_ARRAY,
+};
+
+static int __init register_cgroup_array_map(void)
+{
+       bpf_register_map_type(&cgroup_array_type);
+       return 0;
+}
+late_initcall(register_cgroup_array_map);
+#endif