bpf: Remove bpf_selem_free_fields*_rcu
[platform/kernel/linux-starfive.git] / kernel / bpf / bpf_local_storage.c
index 35f4138..715deaa 100644 (file)
@@ -51,11 +51,21 @@ owner_storage(struct bpf_local_storage_map *smap, void *owner)
        return map->ops->map_owner_storage_ptr(owner);
 }
 
+static bool selem_linked_to_storage_lockless(const struct bpf_local_storage_elem *selem)
+{
+       return !hlist_unhashed_lockless(&selem->snode);
+}
+
 static bool selem_linked_to_storage(const struct bpf_local_storage_elem *selem)
 {
        return !hlist_unhashed(&selem->snode);
 }
 
+static bool selem_linked_to_map_lockless(const struct bpf_local_storage_elem *selem)
+{
+       return !hlist_unhashed_lockless(&selem->map_node);
+}
+
 static bool selem_linked_to_map(const struct bpf_local_storage_elem *selem)
 {
        return !hlist_unhashed(&selem->map_node);
@@ -75,6 +85,7 @@ bpf_selem_alloc(struct bpf_local_storage_map *smap, void *owner,
        if (selem) {
                if (value)
                        copy_map_value(&smap->map, SDATA(selem)->data, value);
+               /* No need to call check_and_init_map_value as memory is zero init */
                return selem;
        }
 
@@ -84,7 +95,7 @@ bpf_selem_alloc(struct bpf_local_storage_map *smap, void *owner,
        return NULL;
 }
 
-void bpf_local_storage_free_rcu(struct rcu_head *rcu)
+static void bpf_local_storage_free_rcu(struct rcu_head *rcu)
 {
        struct bpf_local_storage *local_storage;
 
@@ -98,7 +109,7 @@ void bpf_local_storage_free_rcu(struct rcu_head *rcu)
                kfree_rcu(local_storage, rcu);
 }
 
-static void bpf_selem_free_rcu(struct rcu_head *rcu)
+static void bpf_selem_free_trace_rcu(struct rcu_head *rcu)
 {
        struct bpf_local_storage_elem *selem;
 
@@ -115,7 +126,7 @@ static void bpf_selem_free_rcu(struct rcu_head *rcu)
  */
 static bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_storage,
                                            struct bpf_local_storage_elem *selem,
-                                           bool uncharge_mem, bool use_trace_rcu)
+                                           bool uncharge_mem, bool reuse_now)
 {
        struct bpf_local_storage_map *smap;
        bool free_local_storage;
@@ -159,22 +170,26 @@ static bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_stor
            SDATA(selem))
                RCU_INIT_POINTER(local_storage->cache[smap->cache_idx], NULL);
 
-       if (use_trace_rcu)
-               call_rcu_tasks_trace(&selem->rcu, bpf_selem_free_rcu);
+       bpf_obj_free_fields(smap->map.record, SDATA(selem)->data);
+       if (!reuse_now)
+               call_rcu_tasks_trace(&selem->rcu, bpf_selem_free_trace_rcu);
        else
                kfree_rcu(selem, rcu);
 
+       if (rcu_access_pointer(local_storage->smap) == smap)
+               RCU_INIT_POINTER(local_storage->smap, NULL);
+
        return free_local_storage;
 }
 
-static void __bpf_selem_unlink_storage(struct bpf_local_storage_elem *selem,
-                                      bool use_trace_rcu)
+static void bpf_selem_unlink_storage(struct bpf_local_storage_elem *selem,
+                                    bool reuse_now)
 {
        struct bpf_local_storage *local_storage;
        bool free_local_storage = false;
        unsigned long flags;
 
-       if (unlikely(!selem_linked_to_storage(selem)))
+       if (unlikely(!selem_linked_to_storage_lockless(selem)))
                /* selem has already been unlinked from sk */
                return;
 
@@ -183,11 +198,11 @@ static void __bpf_selem_unlink_storage(struct bpf_local_storage_elem *selem,
        raw_spin_lock_irqsave(&local_storage->lock, flags);
        if (likely(selem_linked_to_storage(selem)))
                free_local_storage = bpf_selem_unlink_storage_nolock(
-                       local_storage, selem, true, use_trace_rcu);
+                       local_storage, selem, true, reuse_now);
        raw_spin_unlock_irqrestore(&local_storage->lock, flags);
 
        if (free_local_storage) {
-               if (use_trace_rcu)
+               if (!reuse_now)
                        call_rcu_tasks_trace(&local_storage->rcu,
                                     bpf_local_storage_free_rcu);
                else
@@ -202,13 +217,13 @@ void bpf_selem_link_storage_nolock(struct bpf_local_storage *local_storage,
        hlist_add_head_rcu(&selem->snode, &local_storage->list);
 }
 
-void bpf_selem_unlink_map(struct bpf_local_storage_elem *selem)
+static void bpf_selem_unlink_map(struct bpf_local_storage_elem *selem)
 {
        struct bpf_local_storage_map *smap;
        struct bpf_local_storage_map_bucket *b;
        unsigned long flags;
 
-       if (unlikely(!selem_linked_to_map(selem)))
+       if (unlikely(!selem_linked_to_map_lockless(selem)))
                /* selem has already be unlinked from smap */
                return;
 
@@ -232,14 +247,14 @@ void bpf_selem_link_map(struct bpf_local_storage_map *smap,
        raw_spin_unlock_irqrestore(&b->lock, flags);
 }
 
-void bpf_selem_unlink(struct bpf_local_storage_elem *selem, bool use_trace_rcu)
+void bpf_selem_unlink(struct bpf_local_storage_elem *selem, bool reuse_now)
 {
        /* Always unlink from map before unlinking from local_storage
         * because selem will be freed after successfully unlinked from
         * the local_storage.
         */
        bpf_selem_unlink_map(selem);
-       __bpf_selem_unlink_storage(selem, use_trace_rcu);
+       bpf_selem_unlink_storage(selem, reuse_now);
 }
 
 /* If cacheit_lockit is false, this lookup function is lockless */
@@ -319,6 +334,7 @@ int bpf_local_storage_alloc(void *owner,
                goto uncharge;
        }
 
+       RCU_INIT_POINTER(storage->smap, smap);
        INIT_HLIST_HEAD(&storage->list);
        raw_spin_lock_init(&storage->lock);
        storage->owner = owner;
@@ -420,7 +436,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap,
                err = check_flags(old_sdata, map_flags);
                if (err)
                        return ERR_PTR(err);
-               if (old_sdata && selem_linked_to_storage(SELEM(old_sdata))) {
+               if (old_sdata && selem_linked_to_storage_lockless(SELEM(old_sdata))) {
                        copy_map_value_locked(&smap->map, old_sdata->data,
                                              value, false);
                        return old_sdata;
@@ -485,7 +501,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap,
        if (old_sdata) {
                bpf_selem_unlink_map(SELEM(old_sdata));
                bpf_selem_unlink_storage_nolock(local_storage, SELEM(old_sdata),
-                                               false, true);
+                                               false, false);
        }
 
 unlock:
@@ -552,40 +568,6 @@ int bpf_local_storage_map_alloc_check(union bpf_attr *attr)
        return 0;
 }
 
-static struct bpf_local_storage_map *__bpf_local_storage_map_alloc(union bpf_attr *attr)
-{
-       struct bpf_local_storage_map *smap;
-       unsigned int i;
-       u32 nbuckets;
-
-       smap = bpf_map_area_alloc(sizeof(*smap), NUMA_NO_NODE);
-       if (!smap)
-               return ERR_PTR(-ENOMEM);
-       bpf_map_init_from_attr(&smap->map, attr);
-
-       nbuckets = roundup_pow_of_two(num_possible_cpus());
-       /* Use at least 2 buckets, select_bucket() is undefined behavior with 1 bucket */
-       nbuckets = max_t(u32, 2, nbuckets);
-       smap->bucket_log = ilog2(nbuckets);
-
-       smap->buckets = bpf_map_kvcalloc(&smap->map, sizeof(*smap->buckets),
-                                        nbuckets, GFP_USER | __GFP_NOWARN);
-       if (!smap->buckets) {
-               bpf_map_area_free(smap);
-               return ERR_PTR(-ENOMEM);
-       }
-
-       for (i = 0; i < nbuckets; i++) {
-               INIT_HLIST_HEAD(&smap->buckets[i].list);
-               raw_spin_lock_init(&smap->buckets[i].lock);
-       }
-
-       smap->elem_size = offsetof(struct bpf_local_storage_elem,
-                                  sdata.data[attr->value_size]);
-
-       return smap;
-}
-
 int bpf_local_storage_map_check_btf(const struct bpf_map *map,
                                    const struct btf *btf,
                                    const struct btf_type *key_type,
@@ -603,11 +585,12 @@ int bpf_local_storage_map_check_btf(const struct bpf_map *map,
        return 0;
 }
 
-bool bpf_local_storage_unlink_nolock(struct bpf_local_storage *local_storage)
+void bpf_local_storage_destroy(struct bpf_local_storage *local_storage)
 {
        struct bpf_local_storage_elem *selem;
        bool free_storage = false;
        struct hlist_node *n;
+       unsigned long flags;
 
        /* Neither the bpf_prog nor the bpf_map's syscall
         * could be modifying the local_storage->list now.
@@ -618,6 +601,7 @@ bool bpf_local_storage_unlink_nolock(struct bpf_local_storage *local_storage)
         * when unlinking elem from the local_storage->list and
         * the map's bucket->list.
         */
+       raw_spin_lock_irqsave(&local_storage->lock, flags);
        hlist_for_each_entry_safe(selem, n, &local_storage->list, snode) {
                /* Always unlink from map before unlinking from
                 * local_storage.
@@ -630,10 +614,22 @@ bool bpf_local_storage_unlink_nolock(struct bpf_local_storage *local_storage)
                 * of the loop will set the free_cgroup_storage to true.
                 */
                free_storage = bpf_selem_unlink_storage_nolock(
-                       local_storage, selem, false, false);
+                       local_storage, selem, false, true);
        }
+       raw_spin_unlock_irqrestore(&local_storage->lock, flags);
+
+       if (free_storage)
+               kfree_rcu(local_storage, rcu);
+}
+
+u64 bpf_local_storage_map_mem_usage(const struct bpf_map *map)
+{
+       struct bpf_local_storage_map *smap = (struct bpf_local_storage_map *)map;
+       u64 usage = sizeof(*smap);
 
-       return free_storage;
+       /* The dynamically callocated selems are not counted currently. */
+       usage += sizeof(*smap->buckets) * (1ULL << smap->bucket_log);
+       return usage;
 }
 
 struct bpf_map *
@@ -641,10 +637,33 @@ bpf_local_storage_map_alloc(union bpf_attr *attr,
                            struct bpf_local_storage_cache *cache)
 {
        struct bpf_local_storage_map *smap;
+       unsigned int i;
+       u32 nbuckets;
+
+       smap = bpf_map_area_alloc(sizeof(*smap), NUMA_NO_NODE);
+       if (!smap)
+               return ERR_PTR(-ENOMEM);
+       bpf_map_init_from_attr(&smap->map, attr);
+
+       nbuckets = roundup_pow_of_two(num_possible_cpus());
+       /* Use at least 2 buckets, select_bucket() is undefined behavior with 1 bucket */
+       nbuckets = max_t(u32, 2, nbuckets);
+       smap->bucket_log = ilog2(nbuckets);
+
+       smap->buckets = bpf_map_kvcalloc(&smap->map, sizeof(*smap->buckets),
+                                        nbuckets, GFP_USER | __GFP_NOWARN);
+       if (!smap->buckets) {
+               bpf_map_area_free(smap);
+               return ERR_PTR(-ENOMEM);
+       }
 
-       smap = __bpf_local_storage_map_alloc(attr);
-       if (IS_ERR(smap))
-               return ERR_CAST(smap);
+       for (i = 0; i < nbuckets; i++) {
+               INIT_HLIST_HEAD(&smap->buckets[i].list);
+               raw_spin_lock_init(&smap->buckets[i].lock);
+       }
+
+       smap->elem_size = offsetof(struct bpf_local_storage_elem,
+                                  sdata.data[attr->value_size]);
 
        smap->cache_idx = bpf_local_storage_cache_idx_get(cache);
        return &smap->map;
@@ -689,7 +708,7 @@ void bpf_local_storage_map_free(struct bpf_map *map,
                                migrate_disable();
                                this_cpu_inc(*busy_counter);
                        }
-                       bpf_selem_unlink(selem, false);
+                       bpf_selem_unlink(selem, true);
                        if (busy_counter) {
                                this_cpu_dec(*busy_counter);
                                migrate_enable();