Commit 1adddc97 authored by Kui-Feng Lee's avatar Kui-Feng Lee Committed by Martin KaFai Lau

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: default avatarKui-Feng Lee <thinker.li@gmail.com>
Link: https://lore.kernel.org/r/20240530065946.979330-4-thinker.li@gmail.comSigned-off-by: default avatarMartin KaFai Lau <martin.lau@kernel.org>
parent 6fb2544e
...@@ -1612,6 +1612,7 @@ struct bpf_link_ops { ...@@ -1612,6 +1612,7 @@ struct bpf_link_ops {
struct bpf_link_info *info); struct bpf_link_info *info);
int (*update_map)(struct bpf_link *link, struct bpf_map *new_map, int (*update_map)(struct bpf_link *link, struct bpf_map *new_map,
struct bpf_map *old_map); struct bpf_map *old_map);
__poll_t (*poll)(struct file *file, struct poll_table_struct *pts);
}; };
struct bpf_tramp_link { struct bpf_tramp_link {
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/btf_ids.h> #include <linux/btf_ids.h>
#include <linux/rcupdate_wait.h> #include <linux/rcupdate_wait.h>
#include <linux/poll.h>
struct bpf_struct_ops_value { struct bpf_struct_ops_value {
struct bpf_struct_ops_common_value common; struct bpf_struct_ops_common_value common;
...@@ -56,6 +57,7 @@ struct bpf_struct_ops_map { ...@@ -56,6 +57,7 @@ struct bpf_struct_ops_map {
struct bpf_struct_ops_link { struct bpf_struct_ops_link {
struct bpf_link link; struct bpf_link link;
struct bpf_map __rcu *map; struct bpf_map __rcu *map;
wait_queue_head_t wait_hup;
}; };
static DEFINE_MUTEX(update_mutex); static DEFINE_MUTEX(update_mutex);
...@@ -1167,15 +1169,28 @@ static int bpf_struct_ops_map_link_detach(struct bpf_link *link) ...@@ -1167,15 +1169,28 @@ static int bpf_struct_ops_map_link_detach(struct bpf_link *link)
mutex_unlock(&update_mutex); mutex_unlock(&update_mutex);
wake_up_interruptible_poll(&st_link->wait_hup, EPOLLHUP);
return 0; 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 = { static const struct bpf_link_ops bpf_struct_ops_map_lops = {
.dealloc = bpf_struct_ops_map_link_dealloc, .dealloc = bpf_struct_ops_map_link_dealloc,
.detach = bpf_struct_ops_map_link_detach, .detach = bpf_struct_ops_map_link_detach,
.show_fdinfo = bpf_struct_ops_map_link_show_fdinfo, .show_fdinfo = bpf_struct_ops_map_link_show_fdinfo,
.fill_link_info = bpf_struct_ops_map_link_fill_link_info, .fill_link_info = bpf_struct_ops_map_link_fill_link_info,
.update_map = bpf_struct_ops_map_link_update, .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) int bpf_struct_ops_link_create(union bpf_attr *attr)
...@@ -1208,6 +1223,8 @@ 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) if (err)
goto err_out; goto err_out;
init_waitqueue_head(&link->wait_hup);
/* Hold the update_mutex such that the subsystem cannot /* Hold the update_mutex such that the subsystem cannot
* do link->ops->detach() before the link is fully initialized. * 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) ...@@ -3150,6 +3150,13 @@ static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
} }
#endif #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 = { static const struct file_operations bpf_link_fops = {
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
.show_fdinfo = bpf_link_show_fdinfo, .show_fdinfo = bpf_link_show_fdinfo,
...@@ -3159,6 +3166,16 @@ static const struct file_operations bpf_link_fops = { ...@@ -3159,6 +3166,16 @@ static const struct file_operations bpf_link_fops = {
.write = bpf_dummy_write, .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) static int bpf_link_alloc_id(struct bpf_link *link)
{ {
int id; int id;
...@@ -3201,7 +3218,9 @@ int bpf_link_prime(struct bpf_link *link, struct bpf_link_primer *primer) ...@@ -3201,7 +3218,9 @@ int bpf_link_prime(struct bpf_link *link, struct bpf_link_primer *primer)
return id; 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)) { if (IS_ERR(file)) {
bpf_link_free_id(id); bpf_link_free_id(id);
put_unused_fd(fd); put_unused_fd(fd);
...@@ -3229,7 +3248,9 @@ int bpf_link_settle(struct bpf_link_primer *primer) ...@@ -3229,7 +3248,9 @@ int bpf_link_settle(struct bpf_link_primer *primer)
int bpf_link_new_fd(struct bpf_link *link) 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) struct bpf_link *bpf_link_get_from_fd(u32 ufd)
...@@ -3239,7 +3260,7 @@ 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) if (!f.file)
return ERR_PTR(-EBADF); 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); fdput(f);
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
...@@ -4971,7 +4992,7 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr, ...@@ -4971,7 +4992,7 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
uattr); uattr);
else if (f.file->f_op == &btf_fops) else if (f.file->f_op == &btf_fops)
err = bpf_btf_get_info_by_fd(f.file, f.file->private_data, attr, uattr); 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, err = bpf_link_get_info_by_fd(f.file, f.file->private_data,
attr, uattr); attr, uattr);
else else
...@@ -5106,7 +5127,7 @@ static int bpf_task_fd_query(const union bpf_attr *attr, ...@@ -5106,7 +5127,7 @@ static int bpf_task_fd_query(const union bpf_attr *attr,
if (!file) if (!file)
return -EBADF; 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; struct bpf_link *link = file->private_data;
if (link->ops == &bpf_raw_tp_link_lops) { if (link->ops == &bpf_raw_tp_link_lops) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment