bpf: support epoll from bpf struct_ops links.
Add epoll support to bpf struct_ops links to trigger EPOLLHUP event upon detachment. This patch implements the "poll" of the "struct file_operations" for BPF links and introduces a new "poll" operator in the "struct bpf_link_ops". By implementing "poll" of "struct bpf_link_ops" for the links of struct_ops, the file descriptor of a struct_ops link can be added to an epoll file descriptor to receive EPOLLHUP events. Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com> Link: https://lore.kernel.org/r/20240530065946.979330-4-thinker.li@gmail.com Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
This commit is contained in:
parent
6fb2544ea1
commit
1adddc97aa
@ -1612,6 +1612,7 @@ struct bpf_link_ops {
|
||||
struct bpf_link_info *info);
|
||||
int (*update_map)(struct bpf_link *link, struct bpf_map *new_map,
|
||||
struct bpf_map *old_map);
|
||||
__poll_t (*poll)(struct file *file, struct poll_table_struct *pts);
|
||||
};
|
||||
|
||||
struct bpf_tramp_link {
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/btf_ids.h>
|
||||
#include <linux/rcupdate_wait.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
struct bpf_struct_ops_value {
|
||||
struct bpf_struct_ops_common_value common;
|
||||
@ -56,6 +57,7 @@ struct bpf_struct_ops_map {
|
||||
struct bpf_struct_ops_link {
|
||||
struct bpf_link link;
|
||||
struct bpf_map __rcu *map;
|
||||
wait_queue_head_t wait_hup;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(update_mutex);
|
||||
@ -1167,15 +1169,28 @@ static int bpf_struct_ops_map_link_detach(struct bpf_link *link)
|
||||
|
||||
mutex_unlock(&update_mutex);
|
||||
|
||||
wake_up_interruptible_poll(&st_link->wait_hup, EPOLLHUP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __poll_t bpf_struct_ops_map_link_poll(struct file *file,
|
||||
struct poll_table_struct *pts)
|
||||
{
|
||||
struct bpf_struct_ops_link *st_link = file->private_data;
|
||||
|
||||
poll_wait(file, &st_link->wait_hup, pts);
|
||||
|
||||
return rcu_access_pointer(st_link->map) ? 0 : EPOLLHUP;
|
||||
}
|
||||
|
||||
static const struct bpf_link_ops bpf_struct_ops_map_lops = {
|
||||
.dealloc = bpf_struct_ops_map_link_dealloc,
|
||||
.detach = bpf_struct_ops_map_link_detach,
|
||||
.show_fdinfo = bpf_struct_ops_map_link_show_fdinfo,
|
||||
.fill_link_info = bpf_struct_ops_map_link_fill_link_info,
|
||||
.update_map = bpf_struct_ops_map_link_update,
|
||||
.poll = bpf_struct_ops_map_link_poll,
|
||||
};
|
||||
|
||||
int bpf_struct_ops_link_create(union bpf_attr *attr)
|
||||
@ -1208,6 +1223,8 @@ int bpf_struct_ops_link_create(union bpf_attr *attr)
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
init_waitqueue_head(&link->wait_hup);
|
||||
|
||||
/* Hold the update_mutex such that the subsystem cannot
|
||||
* do link->ops->detach() before the link is fully initialized.
|
||||
*/
|
||||
|
@ -3150,6 +3150,13 @@ static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
|
||||
}
|
||||
#endif
|
||||
|
||||
static __poll_t bpf_link_poll(struct file *file, struct poll_table_struct *pts)
|
||||
{
|
||||
struct bpf_link *link = file->private_data;
|
||||
|
||||
return link->ops->poll(file, pts);
|
||||
}
|
||||
|
||||
static const struct file_operations bpf_link_fops = {
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show_fdinfo = bpf_link_show_fdinfo,
|
||||
@ -3159,6 +3166,16 @@ static const struct file_operations bpf_link_fops = {
|
||||
.write = bpf_dummy_write,
|
||||
};
|
||||
|
||||
static const struct file_operations bpf_link_fops_poll = {
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show_fdinfo = bpf_link_show_fdinfo,
|
||||
#endif
|
||||
.release = bpf_link_release,
|
||||
.read = bpf_dummy_read,
|
||||
.write = bpf_dummy_write,
|
||||
.poll = bpf_link_poll,
|
||||
};
|
||||
|
||||
static int bpf_link_alloc_id(struct bpf_link *link)
|
||||
{
|
||||
int id;
|
||||
@ -3201,7 +3218,9 @@ int bpf_link_prime(struct bpf_link *link, struct bpf_link_primer *primer)
|
||||
return id;
|
||||
}
|
||||
|
||||
file = anon_inode_getfile("bpf_link", &bpf_link_fops, link, O_CLOEXEC);
|
||||
file = anon_inode_getfile("bpf_link",
|
||||
link->ops->poll ? &bpf_link_fops_poll : &bpf_link_fops,
|
||||
link, O_CLOEXEC);
|
||||
if (IS_ERR(file)) {
|
||||
bpf_link_free_id(id);
|
||||
put_unused_fd(fd);
|
||||
@ -3229,7 +3248,9 @@ int bpf_link_settle(struct bpf_link_primer *primer)
|
||||
|
||||
int bpf_link_new_fd(struct bpf_link *link)
|
||||
{
|
||||
return anon_inode_getfd("bpf-link", &bpf_link_fops, link, O_CLOEXEC);
|
||||
return anon_inode_getfd("bpf-link",
|
||||
link->ops->poll ? &bpf_link_fops_poll : &bpf_link_fops,
|
||||
link, O_CLOEXEC);
|
||||
}
|
||||
|
||||
struct bpf_link *bpf_link_get_from_fd(u32 ufd)
|
||||
@ -3239,7 +3260,7 @@ struct bpf_link *bpf_link_get_from_fd(u32 ufd)
|
||||
|
||||
if (!f.file)
|
||||
return ERR_PTR(-EBADF);
|
||||
if (f.file->f_op != &bpf_link_fops) {
|
||||
if (f.file->f_op != &bpf_link_fops && f.file->f_op != &bpf_link_fops_poll) {
|
||||
fdput(f);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
@ -4971,7 +4992,7 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
|
||||
uattr);
|
||||
else if (f.file->f_op == &btf_fops)
|
||||
err = bpf_btf_get_info_by_fd(f.file, f.file->private_data, attr, uattr);
|
||||
else if (f.file->f_op == &bpf_link_fops)
|
||||
else if (f.file->f_op == &bpf_link_fops || f.file->f_op == &bpf_link_fops_poll)
|
||||
err = bpf_link_get_info_by_fd(f.file, f.file->private_data,
|
||||
attr, uattr);
|
||||
else
|
||||
@ -5106,7 +5127,7 @@ static int bpf_task_fd_query(const union bpf_attr *attr,
|
||||
if (!file)
|
||||
return -EBADF;
|
||||
|
||||
if (file->f_op == &bpf_link_fops) {
|
||||
if (file->f_op == &bpf_link_fops || file->f_op == &bpf_link_fops_poll) {
|
||||
struct bpf_link *link = file->private_data;
|
||||
|
||||
if (link->ops == &bpf_raw_tp_link_lops) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user