bpf: Remove bpf_selem_free_fields*_rcu
[platform/kernel/linux-starfive.git] / kernel / bpf / bpf_local_storage.c
index 2bdd722..715deaa 100644 (file)
@@ -95,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;
 
@@ -109,30 +109,15 @@ 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;
 
        selem = container_of(rcu, struct bpf_local_storage_elem, rcu);
-       /* The can_use_smap bool is set whenever we need to free additional
-        * fields in selem data before freeing selem. bpf_local_storage_map_free
-        * only executes rcu_barrier to wait for RCU callbacks when it has
-        * special fields, hence we can only conditionally dereference smap, as
-        * by this time the map might have already been freed without waiting
-        * for our call_rcu callback if it did not have any special fields.
-        */
-       if (selem->can_use_smap)
-               bpf_obj_free_fields(SDATA(selem)->smap->map.record, SDATA(selem)->data);
-       kfree(selem);
-}
-
-static void bpf_selem_free_tasks_trace_rcu(struct rcu_head *rcu)
-{
-       /* Free directly if Tasks Trace RCU GP also implies RCU GP */
        if (rcu_trace_implies_rcu_gp())
-               bpf_selem_free_rcu(rcu);
+               kfree(selem);
        else
-               call_rcu(rcu, bpf_selem_free_rcu);
+               kfree_rcu(selem, rcu);
 }
 
 /* local_storage->lock must be held and selem->local_storage == local_storage.
@@ -141,7 +126,7 @@ static void bpf_selem_free_tasks_trace_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;
@@ -185,16 +170,20 @@ 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_tasks_trace_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
-               call_rcu(&selem->rcu, bpf_selem_free_rcu);
+               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;
@@ -209,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
@@ -228,7 +217,7 @@ 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;
@@ -256,21 +245,16 @@ void bpf_selem_link_map(struct bpf_local_storage_map *smap,
        RCU_INIT_POINTER(SDATA(selem)->smap, smap);
        hlist_add_head_rcu(&selem->map_node, &b->list);
        raw_spin_unlock_irqrestore(&b->lock, flags);
-
-       /* If our data will have special fields, smap will wait for us to use
-        * its record in bpf_selem_free_* RCU callbacks before freeing itself.
-        */
-       selem->can_use_smap = !IS_ERR_OR_NULL(smap->map.record);
 }
 
-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 */
@@ -350,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;
@@ -516,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:
@@ -583,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,
@@ -634,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.
@@ -649,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.
@@ -661,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);
+}
 
-       return free_storage;
+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);
+
+       /* The dynamically callocated selems are not counted currently. */
+       usage += sizeof(*smap->buckets) * (1ULL << smap->bucket_log);
+       return usage;
 }
 
 struct bpf_map *
@@ -672,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);
 
-       smap = __bpf_local_storage_map_alloc(attr);
-       if (IS_ERR(smap))
-               return ERR_CAST(smap);
+       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]);
 
        smap->cache_idx = bpf_local_storage_cache_idx_get(cache);
        return &smap->map;
@@ -720,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();
@@ -744,25 +732,6 @@ void bpf_local_storage_map_free(struct bpf_map *map,
         */
        synchronize_rcu();
 
-       /* Only delay freeing of smap, buckets are not needed anymore */
        kvfree(smap->buckets);
-
-       /* When local storage has special fields, callbacks for
-        * bpf_selem_free_rcu and bpf_selem_free_tasks_trace_rcu will keep using
-        * the map BTF record, we need to execute an RCU barrier to wait for
-        * them as the record will be freed right after our map_free callback.
-        */
-       if (!IS_ERR_OR_NULL(smap->map.record)) {
-               rcu_barrier_tasks_trace();
-               /* We cannot skip rcu_barrier() when rcu_trace_implies_rcu_gp()
-                * is true, because while call_rcu invocation is skipped in that
-                * case in bpf_selem_free_tasks_trace_rcu (and all local storage
-                * maps pass use_trace_rcu = true), there can be call_rcu
-                * callbacks based on use_trace_rcu = false in the earlier while
-                * ((selem = ...)) loop or from bpf_local_storage_unlink_nolock
-                * called from owner's free path.
-                */
-               rcu_barrier();
-       }
        bpf_map_area_free(smap);
 }