mirror of
https://github.com/torvalds/linux.git
synced 2024-12-12 14:12:51 +00:00
bpf: decouple BPF link/attach hook and BPF program sleepable semantics
BPF link's lifecycle protection scheme depends on both BPF hook and BPF program. If *either* of those require RCU Tasks Trace GP, then we need to go through a chain of GPs before putting BPF program refcount and deallocating BPF link memory. This patch adds bpf_link-specific sleepable flag, which can be set to true even if underlying BPF program is not sleepable itself. If either link->sleepable or link->prog->sleepable is true, we'll go through a chain of RCU Tasks Trace GP and RCU GP before putting BPF program and freeing memory. This will be used to protect BPF link for sleepable (faultable) raw tracepoints in the next patch. Link: https://lore.kernel.org/20241101181754.782341-2-andrii@kernel.org Tested-by: Jordan Rife <jrife@google.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
This commit is contained in:
parent
f44ec8733a
commit
61c6fefa92
@ -1598,6 +1598,11 @@ struct bpf_link {
|
|||||||
enum bpf_link_type type;
|
enum bpf_link_type type;
|
||||||
const struct bpf_link_ops *ops;
|
const struct bpf_link_ops *ops;
|
||||||
struct bpf_prog *prog;
|
struct bpf_prog *prog;
|
||||||
|
/* whether BPF link itself has "sleepable" semantics, which can differ
|
||||||
|
* from underlying BPF program having a "sleepable" semantics, as BPF
|
||||||
|
* link's semantics is determined by target attach hook
|
||||||
|
*/
|
||||||
|
bool sleepable;
|
||||||
/* rcu is used before freeing, work can be used to schedule that
|
/* rcu is used before freeing, work can be used to schedule that
|
||||||
* RCU-based freeing before that, so they never overlap
|
* RCU-based freeing before that, so they never overlap
|
||||||
*/
|
*/
|
||||||
@ -1614,8 +1619,10 @@ struct bpf_link_ops {
|
|||||||
*/
|
*/
|
||||||
void (*dealloc)(struct bpf_link *link);
|
void (*dealloc)(struct bpf_link *link);
|
||||||
/* deallocate link resources callback, called after RCU grace period;
|
/* deallocate link resources callback, called after RCU grace period;
|
||||||
* if underlying BPF program is sleepable we go through tasks trace
|
* if either the underlying BPF program is sleepable or BPF link's
|
||||||
* RCU GP and then "classic" RCU GP
|
* target hook is sleepable, we'll go through tasks trace RCU GP and
|
||||||
|
* then "classic" RCU GP; this need for chaining tasks trace and
|
||||||
|
* classic RCU GPs is designated by setting bpf_link->sleepable flag
|
||||||
*/
|
*/
|
||||||
void (*dealloc_deferred)(struct bpf_link *link);
|
void (*dealloc_deferred)(struct bpf_link *link);
|
||||||
int (*detach)(struct bpf_link *link);
|
int (*detach)(struct bpf_link *link);
|
||||||
@ -2362,6 +2369,9 @@ int bpf_prog_new_fd(struct bpf_prog *prog);
|
|||||||
|
|
||||||
void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
|
void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
|
||||||
const struct bpf_link_ops *ops, struct bpf_prog *prog);
|
const struct bpf_link_ops *ops, struct bpf_prog *prog);
|
||||||
|
void bpf_link_init_sleepable(struct bpf_link *link, enum bpf_link_type type,
|
||||||
|
const struct bpf_link_ops *ops, struct bpf_prog *prog,
|
||||||
|
bool sleepable);
|
||||||
int bpf_link_prime(struct bpf_link *link, struct bpf_link_primer *primer);
|
int bpf_link_prime(struct bpf_link *link, struct bpf_link_primer *primer);
|
||||||
int bpf_link_settle(struct bpf_link_primer *primer);
|
int bpf_link_settle(struct bpf_link_primer *primer);
|
||||||
void bpf_link_cleanup(struct bpf_link_primer *primer);
|
void bpf_link_cleanup(struct bpf_link_primer *primer);
|
||||||
@ -2717,6 +2727,12 @@ static inline void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void bpf_link_init_sleepable(struct bpf_link *link, enum bpf_link_type type,
|
||||||
|
const struct bpf_link_ops *ops, struct bpf_prog *prog,
|
||||||
|
bool sleepable)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static inline int bpf_link_prime(struct bpf_link *link,
|
static inline int bpf_link_prime(struct bpf_link *link,
|
||||||
struct bpf_link_primer *primer)
|
struct bpf_link_primer *primer)
|
||||||
{
|
{
|
||||||
|
@ -2933,17 +2933,33 @@ static int bpf_obj_get(const union bpf_attr *attr)
|
|||||||
attr->file_flags);
|
attr->file_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
|
/* bpf_link_init_sleepable() allows to specify whether BPF link itself has
|
||||||
const struct bpf_link_ops *ops, struct bpf_prog *prog)
|
* "sleepable" semantics, which normally would mean that BPF link's attach
|
||||||
|
* hook can dereference link or link's underlying program for some time after
|
||||||
|
* detachment due to RCU Tasks Trace-based lifetime protection scheme.
|
||||||
|
* BPF program itself can be non-sleepable, yet, because it's transitively
|
||||||
|
* reachable through BPF link, its freeing has to be delayed until after RCU
|
||||||
|
* Tasks Trace GP.
|
||||||
|
*/
|
||||||
|
void bpf_link_init_sleepable(struct bpf_link *link, enum bpf_link_type type,
|
||||||
|
const struct bpf_link_ops *ops, struct bpf_prog *prog,
|
||||||
|
bool sleepable)
|
||||||
{
|
{
|
||||||
WARN_ON(ops->dealloc && ops->dealloc_deferred);
|
WARN_ON(ops->dealloc && ops->dealloc_deferred);
|
||||||
atomic64_set(&link->refcnt, 1);
|
atomic64_set(&link->refcnt, 1);
|
||||||
link->type = type;
|
link->type = type;
|
||||||
|
link->sleepable = sleepable;
|
||||||
link->id = 0;
|
link->id = 0;
|
||||||
link->ops = ops;
|
link->ops = ops;
|
||||||
link->prog = prog;
|
link->prog = prog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
|
||||||
|
const struct bpf_link_ops *ops, struct bpf_prog *prog)
|
||||||
|
{
|
||||||
|
bpf_link_init_sleepable(link, type, ops, prog, false);
|
||||||
|
}
|
||||||
|
|
||||||
static void bpf_link_free_id(int id)
|
static void bpf_link_free_id(int id)
|
||||||
{
|
{
|
||||||
if (!id)
|
if (!id)
|
||||||
@ -3008,20 +3024,21 @@ static void bpf_link_defer_dealloc_mult_rcu_gp(struct rcu_head *rcu)
|
|||||||
static void bpf_link_free(struct bpf_link *link)
|
static void bpf_link_free(struct bpf_link *link)
|
||||||
{
|
{
|
||||||
const struct bpf_link_ops *ops = link->ops;
|
const struct bpf_link_ops *ops = link->ops;
|
||||||
bool sleepable = false;
|
|
||||||
|
|
||||||
bpf_link_free_id(link->id);
|
bpf_link_free_id(link->id);
|
||||||
if (link->prog) {
|
/* detach BPF program, clean up used resources */
|
||||||
sleepable = link->prog->sleepable;
|
if (link->prog)
|
||||||
/* detach BPF program, clean up used resources */
|
|
||||||
ops->release(link);
|
ops->release(link);
|
||||||
}
|
|
||||||
if (ops->dealloc_deferred) {
|
if (ops->dealloc_deferred) {
|
||||||
/* schedule BPF link deallocation; if underlying BPF program
|
/* Schedule BPF link deallocation, which will only then
|
||||||
* is sleepable, we need to first wait for RCU tasks trace
|
* trigger putting BPF program refcount.
|
||||||
* sync, then go through "classic" RCU grace period
|
* If underlying BPF program is sleepable or BPF link's target
|
||||||
|
* attach hookpoint is sleepable or otherwise requires RCU GPs
|
||||||
|
* to ensure link and its underlying BPF program is not
|
||||||
|
* reachable anymore, we need to first wait for RCU tasks
|
||||||
|
* trace sync, and then go through "classic" RCU grace period
|
||||||
*/
|
*/
|
||||||
if (sleepable)
|
if (link->sleepable || (link->prog && link->prog->sleepable))
|
||||||
call_rcu_tasks_trace(&link->rcu, bpf_link_defer_dealloc_mult_rcu_gp);
|
call_rcu_tasks_trace(&link->rcu, bpf_link_defer_dealloc_mult_rcu_gp);
|
||||||
else
|
else
|
||||||
call_rcu(&link->rcu, bpf_link_defer_dealloc_rcu_gp);
|
call_rcu(&link->rcu, bpf_link_defer_dealloc_rcu_gp);
|
||||||
|
Loading…
Reference in New Issue
Block a user