[de]register BPF programs through io_uring_register() with new IORING_ATTACH_BPF and IORING_DETACH_BPF commands. Signed-off-by: Pavel Begunkov <asml.silence@xxxxxxxxx> --- fs/io_uring.c | 80 +++++++++++++++++++++++++++++++++++ include/uapi/linux/io_uring.h | 2 + 2 files changed, 82 insertions(+) diff --git a/fs/io_uring.c b/fs/io_uring.c index 2c8904bee386..524cf1eb1cec 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -82,6 +82,7 @@ #include <linux/io_uring.h> #include <linux/blk-cgroup.h> #include <linux/audit.h> +#include <linux/bpf.h> #define CREATE_TRACE_POINTS #include <trace/events/io_uring.h> @@ -249,6 +250,10 @@ struct io_restriction { bool registered; }; +struct io_bpf_prog { + struct bpf_prog *prog; +}; + struct io_sq_data { refcount_t refs; struct mutex lock; @@ -388,6 +393,10 @@ struct io_ring_ctx { unsigned nr_user_bufs; struct io_mapped_ubuf *user_bufs; + /* bpf programs */ + struct io_bpf_prog *bpf_progs; + unsigned nr_bpf_progs; + struct user_struct *user; const struct cred *creds; @@ -8694,6 +8703,67 @@ static void io_req_cache_free(struct list_head *list) } } +static int io_bpf_detach(struct io_ring_ctx *ctx) +{ + int i; + + if (!ctx->nr_bpf_progs) + return -ENXIO; + + for (i = 0; i < ctx->nr_bpf_progs; ++i) { + struct bpf_prog *prog = ctx->bpf_progs[i].prog; + + if (prog) + bpf_prog_put(prog); + } + kfree(ctx->bpf_progs); + ctx->bpf_progs = NULL; + ctx->nr_bpf_progs = 0; + return 0; +} + +static int io_bpf_attach(struct io_ring_ctx *ctx, void __user *arg, + unsigned int nr_args) +{ + u32 __user *fds = arg; + int i, ret = 0; + + if (!nr_args || nr_args > 100) + return -EINVAL; + if (ctx->nr_bpf_progs) + return -EBUSY; + + ctx->bpf_progs = kcalloc(nr_args, sizeof(ctx->bpf_progs[0]), + GFP_KERNEL); + if (!ctx->bpf_progs) + return -ENOMEM; + + for (i = 0; i < nr_args; ++i) { + struct bpf_prog *prog; + u32 fd; + + if (copy_from_user(&fd, &fds[i], sizeof(fd))) { + ret = -EFAULT; + break; + } + if (fd == -1) + continue; + + prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_IOURING); + if (IS_ERR(prog)) { + ret = PTR_ERR(prog); + break; + } + + ctx->bpf_progs[i].prog = prog; + } + + ctx->nr_bpf_progs = i; + if (ret) + io_bpf_detach(ctx); + return ret; +} + static void io_ring_ctx_free(struct io_ring_ctx *ctx) { struct io_submit_state *submit_state = &ctx->submit_state; @@ -8708,6 +8778,7 @@ static void io_ring_ctx_free(struct io_ring_ctx *ctx) io_finish_async(ctx); io_sqe_buffers_unregister(ctx); + io_bpf_detach(ctx); if (ctx->sqo_task) { put_task_struct(ctx->sqo_task); @@ -10151,6 +10222,15 @@ static int __io_uring_register(struct io_ring_ctx *ctx, unsigned opcode, case IORING_REGISTER_RESTRICTIONS: ret = io_register_restrictions(ctx, arg, nr_args); break; + case IORING_ATTACH_BPF: + ret = io_bpf_attach(ctx, arg, nr_args); + break; + case IORING_DETACH_BPF: + ret = -EINVAL; + if (arg || nr_args) + break; + ret = io_bpf_detach(ctx); + break; default: ret = -EINVAL; break; diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index ac4e1738a9af..d95e04d6d316 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -280,6 +280,8 @@ enum { IORING_UNREGISTER_PERSONALITY = 10, IORING_REGISTER_RESTRICTIONS = 11, IORING_REGISTER_ENABLE_RINGS = 12, + IORING_ATTACH_BPF = 13, + IORING_DETACH_BPF = 14, /* this goes last */ IORING_REGISTER_LAST -- 2.24.0