mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
bpf: Make struct_ops tasks_rcu grace period optional
bpf_struct_ops_map_free() currently waits for both a regular RCU grace period and a tasks RCU grace period for every struct_ops map through synchronize_rcu_mult(call_rcu, call_rcu_tasks). A regular RCU grace period is still required for all struct_ops maps because the struct_ops trampoline ksyms requires a rcu grace period (take a look at the list_del_rcu in __bpf_ksym_del). Add a map_free_pre_rcu() callback so the struct_ops map can remove ksyms before bpf_map_put() wait for the regular rcu grace period. The tasks RCU grace period is only needed by tcp_congestion_ops. Add free_after_tasks_rcu_gp only to struct bpf_struct_ops instead of the bpf_map. When CONFIG_TASKS_RCU=n, synchronize_rcu_tasks() is the same as synchronize_rcu(). Since all struct_ops maps now complete a regular RCU grace period before bpf_struct_ops_map_free() runs, skip the extra synchronize_rcu_tasks() call in this case. This cleanup prepares for a later patch that needs to support free_after_mult_rcu_gp. Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org> Signed-off-by: Amery Hung <ameryhung@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com> Reviewed-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://patch.msgid.link/20260917200542.3689605-3-ameryhung@gmail.com
This commit is contained in:
parent
0120895121
commit
5db69b0fbd
|
|
@ -90,6 +90,7 @@ struct bpf_map_ops {
|
|||
struct bpf_map *(*map_alloc)(union bpf_attr *attr);
|
||||
void (*map_release)(struct bpf_map *map, struct file *map_file);
|
||||
void (*map_free)(struct bpf_map *map);
|
||||
void (*map_free_pre_rcu)(struct bpf_map *map);
|
||||
int (*map_get_next_key)(struct bpf_map *map, void *key, void *next_key);
|
||||
void (*map_release_uref)(struct bpf_map *map);
|
||||
void *(*map_lookup_elem_sys_only)(struct bpf_map *map, void *key);
|
||||
|
|
@ -2131,6 +2132,11 @@ struct btf_member;
|
|||
* unloaded while in use.
|
||||
* @name: The name of the struct bpf_struct_ops object.
|
||||
* @func_models: Func models
|
||||
* @free_after_tasks_rcu_gp: Set to true if it needs the bpf core to wait for
|
||||
* a tasks_rcu gp before freeing the struct_ops map
|
||||
* and its progs. It is unnecessary if the @unreg
|
||||
* has waited for the correct rcu gp or the @unreg
|
||||
* has ensured all struct_ops prog has finished running.
|
||||
*/
|
||||
struct bpf_struct_ops {
|
||||
const struct bpf_verifier_ops *verifier_ops;
|
||||
|
|
@ -2149,6 +2155,7 @@ struct bpf_struct_ops {
|
|||
struct module *owner;
|
||||
const char *name;
|
||||
struct btf_func_model func_models[BPF_STRUCT_OPS_MAX_NR_MEMBERS];
|
||||
bool free_after_tasks_rcu_gp;
|
||||
};
|
||||
|
||||
/* Every member of a struct_ops type has an instance even a member is not
|
||||
|
|
|
|||
|
|
@ -1024,9 +1024,18 @@ static void __bpf_struct_ops_map_free(struct bpf_map *map)
|
|||
bpf_map_area_free(st_map);
|
||||
}
|
||||
|
||||
static void bpf_struct_ops_map_free_pre_rcu(struct bpf_map *map)
|
||||
{
|
||||
struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
|
||||
|
||||
bpf_struct_ops_map_del_ksyms(st_map);
|
||||
}
|
||||
|
||||
static void bpf_struct_ops_map_free(struct bpf_map *map)
|
||||
{
|
||||
struct bpf_struct_ops_map *st_map = (struct bpf_struct_ops_map *)map;
|
||||
struct bpf_struct_ops *st_ops = st_map->st_ops_desc->st_ops;
|
||||
bool tasks_rcu = st_ops->free_after_tasks_rcu_gp;
|
||||
|
||||
/* st_ops->owner was acquired during map_alloc to implicitly holds
|
||||
* the btf's refcnt. The acquire was only done when btf_is_module()
|
||||
|
|
@ -1037,24 +1046,8 @@ static void bpf_struct_ops_map_free(struct bpf_map *map)
|
|||
|
||||
bpf_struct_ops_map_dissoc_progs(st_map);
|
||||
|
||||
bpf_struct_ops_map_del_ksyms(st_map);
|
||||
|
||||
/* The struct_ops's function may switch to another struct_ops.
|
||||
*
|
||||
* For example, bpf_tcp_cc_x->init() may switch to
|
||||
* another tcp_cc_y by calling
|
||||
* setsockopt(TCP_CONGESTION, "tcp_cc_y").
|
||||
* During the switch, bpf_struct_ops_put(tcp_cc_x) is called
|
||||
* and its refcount may reach 0 which then free its
|
||||
* trampoline image while tcp_cc_x is still running.
|
||||
*
|
||||
* A vanilla rcu gp is to wait for all bpf-tcp-cc prog
|
||||
* to finish. bpf-tcp-cc prog is non sleepable.
|
||||
* A rcu_tasks gp is to wait for the last few insn
|
||||
* in the tramopline image to finish before releasing
|
||||
* the trampoline image.
|
||||
*/
|
||||
synchronize_rcu_mult(call_rcu, call_rcu_tasks);
|
||||
if (tasks_rcu && IS_ENABLED(CONFIG_TASKS_RCU))
|
||||
synchronize_rcu_tasks();
|
||||
|
||||
__bpf_struct_ops_map_free(map);
|
||||
}
|
||||
|
|
@ -1163,6 +1156,7 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
|
|||
|
||||
mutex_init(&st_map->lock);
|
||||
bpf_map_init_from_attr(map, attr);
|
||||
map->free_after_rcu_gp = true;
|
||||
|
||||
return map;
|
||||
|
||||
|
|
@ -1195,6 +1189,7 @@ const struct bpf_map_ops bpf_struct_ops_map_ops = {
|
|||
.map_alloc_check = bpf_struct_ops_map_alloc_check,
|
||||
.map_alloc = bpf_struct_ops_map_alloc,
|
||||
.map_free = bpf_struct_ops_map_free,
|
||||
.map_free_pre_rcu = bpf_struct_ops_map_free_pre_rcu,
|
||||
.map_get_next_key = bpf_struct_ops_map_get_next_key,
|
||||
.map_lookup_elem = bpf_struct_ops_map_lookup_elem,
|
||||
.map_delete_elem = bpf_struct_ops_map_delete_elem,
|
||||
|
|
|
|||
|
|
@ -954,6 +954,9 @@ void bpf_map_put(struct bpf_map *map)
|
|||
/* bpf_map_free_id() must be called first */
|
||||
bpf_map_free_id(map);
|
||||
|
||||
if (map->ops->map_free_pre_rcu)
|
||||
map->ops->map_free_pre_rcu(map);
|
||||
|
||||
WARN_ON_ONCE(atomic64_read(&map->sleepable_refcnt));
|
||||
/* RCU tasks trace grace period implies RCU grace period. */
|
||||
if (READ_ONCE(map->free_after_mult_rcu_gp))
|
||||
|
|
|
|||
|
|
@ -339,6 +339,22 @@ static struct bpf_struct_ops bpf_tcp_congestion_ops = {
|
|||
.validate = bpf_tcp_ca_validate,
|
||||
.name = "tcp_congestion_ops",
|
||||
.cfi_stubs = &__bpf_ops_tcp_congestion_ops,
|
||||
/* The struct_ops's function may switch to another struct_ops.
|
||||
*
|
||||
* For example, bpf_tcp_cc_x->init() may switch to
|
||||
* another tcp_cc_y by calling
|
||||
* setsockopt(TCP_CONGESTION, "tcp_cc_y").
|
||||
* During the switch, bpf_struct_ops_put(tcp_cc_x) is called
|
||||
* and its refcount may reach 0 which then free its
|
||||
* trampoline image while tcp_cc_x is still running.
|
||||
*
|
||||
* A vanilla rcu gp is to wait for all bpf-tcp-cc prog
|
||||
* to finish. bpf-tcp-cc prog is non sleepable.
|
||||
* A rcu_tasks gp is to wait for the last few insn
|
||||
* in the tramopline image to finish before releasing
|
||||
* the trampoline image.
|
||||
*/
|
||||
.free_after_tasks_rcu_gp = true,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user