@@ -100,6 +100,7 @@ struct bpf_htab {
struct percpu_counter pcount;
atomic_t count;
bool use_percpu_counter;
+ bool delayed_free;
u32 n_buckets; /* number of hash buckets */
u32 elem_size; /* size of each element in bytes */
u32 hashrnd;
@@ -120,14 +121,24 @@ struct htab_elem {
};
};
union {
- /* pointer to per-cpu pointer */
- void *ptr_to_pptr;
+ struct {
+ /* pointer to per-cpu pointer */
+ void *ptr_to_pptr;
+ struct bpf_mem_alloc *ma;
+ struct rcu_head rcu;
+ };
struct bpf_lru_node lru_node;
};
u32 hash;
char key[] __aligned(8);
};
+static int reuse_flag;
+module_param(reuse_flag, int, 0644);
+
+static bool delayed_free;
+module_param(delayed_free, bool, 0644);
+
static inline bool htab_is_prealloc(const struct bpf_htab *htab)
{
return !(htab->map.map_flags & BPF_F_NO_PREALLOC);
@@ -539,6 +550,7 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
htab_init_buckets(htab);
+ htab->delayed_free = delayed_free;
/* compute_batch_value() computes batch value as num_online_cpus() * 2
* and __percpu_counter_compare() needs
* htab->max_entries - cur_number_of_elems to be more than batch * num_online_cpus()
@@ -576,7 +588,7 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
goto free_prealloc;
}
} else {
- err = bpf_mem_alloc_init(&htab->ma, htab->elem_size, 0);
+ err = bpf_mem_alloc_init(&htab->ma, htab->elem_size, reuse_flag);
if (err)
goto free_map_locked;
if (percpu) {
@@ -878,12 +890,24 @@ static int htab_map_get_next_key(struct bpf_map *map, void *key, void *next_key)
return -ENOENT;
}
-static void htab_elem_free(struct bpf_htab *htab, struct htab_elem *l)
+static void htab_elem_free_rcu(struct rcu_head *rcu)
+{
+ struct htab_elem *l = container_of(rcu, struct htab_elem, rcu);
+
+ bpf_mem_cache_free(l->ma, l);
+}
+
+static void htab_elem_free(struct bpf_htab *htab, struct htab_elem *l, bool destroy)
{
check_and_free_fields(htab, l);
if (htab->map.map_type == BPF_MAP_TYPE_PERCPU_HASH)
bpf_mem_cache_free(&htab->pcpu_ma, l->ptr_to_pptr);
- bpf_mem_cache_free(&htab->ma, l);
+ if (destroy || !htab->delayed_free) {
+ bpf_mem_cache_free(&htab->ma, l);
+ return;
+ }
+ l->ma = &htab->ma;
+ call_rcu(&l->rcu, htab_elem_free_rcu);
}
static void htab_put_fd_value(struct bpf_htab *htab, struct htab_elem *l)
@@ -931,7 +955,7 @@ static void free_htab_elem(struct bpf_htab *htab, struct htab_elem *l)
__pcpu_freelist_push(&htab->freelist, &l->fnode);
} else {
dec_elem_count(htab);
- htab_elem_free(htab, l);
+ htab_elem_free(htab, l, false);
}
}
@@ -1468,7 +1492,7 @@ static void delete_all_elements(struct bpf_htab *htab)
hlist_nulls_for_each_entry_safe(l, n, head, hash_node) {
hlist_nulls_del_rcu(&l->hash_node);
- htab_elem_free(htab, l);
+ htab_elem_free(htab, l, true);
}
}
migrate_enable();
@@ -1522,6 +1546,8 @@ static void htab_map_free(struct bpf_map *map)
* during bpf_mem_alloc_destroy().
*/
if (!htab_is_prealloc(htab)) {
+ if (htab->delayed_free)
+ rcu_barrier();
delete_all_elements(htab);
} else {
htab_free_prealloced_fields(htab);