mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
io_uring_enter() and io_uring_register() end up having duplicated code for getting a ctx from a passed in file descriptor, for either a registered ring descriptor or a normal file descriptor. Move the io_uring_register_get_file() into io_uring.c and name it a bit more generically, and use it from both callsites rather than have that logic and handling duplicated. Signed-off-by: Jens Axboe <axboe@kernel.dk>
271 lines
5.9 KiB
C
271 lines
5.9 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#include <linux/mutex.h>
|
|
#include <linux/bpf.h>
|
|
#include <linux/bpf_verifier.h>
|
|
|
|
#include "io_uring.h"
|
|
#include "register.h"
|
|
#include "loop.h"
|
|
#include "memmap.h"
|
|
#include "bpf-ops.h"
|
|
|
|
static DEFINE_MUTEX(io_bpf_ctrl_mutex);
|
|
static const struct btf_type *loop_params_type;
|
|
|
|
__bpf_kfunc_start_defs();
|
|
|
|
__bpf_kfunc int bpf_io_uring_submit_sqes(struct io_ring_ctx *ctx, u32 nr)
|
|
{
|
|
return io_submit_sqes(ctx, nr);
|
|
}
|
|
|
|
__bpf_kfunc
|
|
__u8 *bpf_io_uring_get_region(struct io_ring_ctx *ctx, __u32 region_id,
|
|
const size_t rdwr_buf_size)
|
|
{
|
|
struct io_mapped_region *r;
|
|
|
|
lockdep_assert_held(&ctx->uring_lock);
|
|
|
|
switch (region_id) {
|
|
case IOU_REGION_MEM:
|
|
r = &ctx->param_region;
|
|
break;
|
|
case IOU_REGION_CQ:
|
|
r = &ctx->ring_region;
|
|
break;
|
|
case IOU_REGION_SQ:
|
|
r = &ctx->sq_region;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
if (unlikely(rdwr_buf_size > io_region_size(r)))
|
|
return NULL;
|
|
return io_region_get_ptr(r);
|
|
}
|
|
|
|
__bpf_kfunc_end_defs();
|
|
|
|
BTF_KFUNCS_START(io_uring_kfunc_set)
|
|
BTF_ID_FLAGS(func, bpf_io_uring_submit_sqes, KF_SLEEPABLE);
|
|
BTF_ID_FLAGS(func, bpf_io_uring_get_region, KF_RET_NULL);
|
|
BTF_KFUNCS_END(io_uring_kfunc_set)
|
|
|
|
static const struct btf_kfunc_id_set bpf_io_uring_kfunc_set = {
|
|
.owner = THIS_MODULE,
|
|
.set = &io_uring_kfunc_set,
|
|
};
|
|
|
|
static int io_bpf_ops__loop_step(struct io_ring_ctx *ctx,
|
|
struct iou_loop_params *lp)
|
|
{
|
|
return IOU_LOOP_STOP;
|
|
}
|
|
|
|
static struct io_uring_bpf_ops io_bpf_ops_stubs = {
|
|
.loop_step = io_bpf_ops__loop_step,
|
|
};
|
|
|
|
static bool bpf_io_is_valid_access(int off, int size,
|
|
enum bpf_access_type type,
|
|
const struct bpf_prog *prog,
|
|
struct bpf_insn_access_aux *info)
|
|
{
|
|
if (type != BPF_READ)
|
|
return false;
|
|
if (off < 0 || off >= sizeof(__u64) * MAX_BPF_FUNC_ARGS)
|
|
return false;
|
|
if (off % size != 0)
|
|
return false;
|
|
|
|
return btf_ctx_access(off, size, type, prog, info);
|
|
}
|
|
|
|
static int bpf_io_btf_struct_access(struct bpf_verifier_log *log,
|
|
const struct bpf_reg_state *reg, int off,
|
|
int size)
|
|
{
|
|
const struct btf_type *t = btf_type_by_id(reg->btf, reg->btf_id);
|
|
|
|
if (t == loop_params_type) {
|
|
if (off + size <= offsetofend(struct iou_loop_params, cq_wait_idx))
|
|
return SCALAR_VALUE;
|
|
}
|
|
|
|
return -EACCES;
|
|
}
|
|
|
|
static const struct bpf_verifier_ops bpf_io_verifier_ops = {
|
|
.get_func_proto = bpf_base_func_proto,
|
|
.is_valid_access = bpf_io_is_valid_access,
|
|
.btf_struct_access = bpf_io_btf_struct_access,
|
|
};
|
|
|
|
static const struct btf_type *
|
|
io_lookup_struct_type(struct btf *btf, const char *name)
|
|
{
|
|
s32 type_id;
|
|
|
|
type_id = btf_find_by_name_kind(btf, name, BTF_KIND_STRUCT);
|
|
if (type_id < 0)
|
|
return NULL;
|
|
return btf_type_by_id(btf, type_id);
|
|
}
|
|
|
|
static int bpf_io_init(struct btf *btf)
|
|
{
|
|
int ret;
|
|
|
|
loop_params_type = io_lookup_struct_type(btf, "iou_loop_params");
|
|
if (!loop_params_type) {
|
|
pr_err("io_uring: Failed to locate iou_loop_params\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS,
|
|
&bpf_io_uring_kfunc_set);
|
|
if (ret) {
|
|
pr_err("io_uring: Failed to register kfuncs (%d)\n", ret);
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int bpf_io_check_member(const struct btf_type *t,
|
|
const struct btf_member *member,
|
|
const struct bpf_prog *prog)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int bpf_io_init_member(const struct btf_type *t,
|
|
const struct btf_member *member,
|
|
void *kdata, const void *udata)
|
|
{
|
|
u32 moff = __btf_member_bit_offset(t, member) / 8;
|
|
const struct io_uring_bpf_ops *uops = udata;
|
|
struct io_uring_bpf_ops *ops = kdata;
|
|
|
|
switch (moff) {
|
|
case offsetof(struct io_uring_bpf_ops, ring_fd):
|
|
ops->ring_fd = uops->ring_fd;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int io_install_bpf(struct io_ring_ctx *ctx, struct io_uring_bpf_ops *ops)
|
|
{
|
|
if (ctx->flags & (IORING_SETUP_SQPOLL | IORING_SETUP_IOPOLL))
|
|
return -EOPNOTSUPP;
|
|
if (!(ctx->flags & IORING_SETUP_DEFER_TASKRUN))
|
|
return -EOPNOTSUPP;
|
|
|
|
if (ctx->bpf_ops)
|
|
return -EBUSY;
|
|
if (WARN_ON_ONCE(!ops->loop_step))
|
|
return -EINVAL;
|
|
|
|
ops->priv = ctx;
|
|
ctx->bpf_ops = ops;
|
|
ctx->loop_step = ops->loop_step;
|
|
return 0;
|
|
}
|
|
|
|
static int bpf_io_reg(void *kdata, struct bpf_link *link)
|
|
{
|
|
struct io_uring_bpf_ops *ops = kdata;
|
|
struct io_ring_ctx *ctx;
|
|
struct file *file;
|
|
int ret = -EBUSY;
|
|
|
|
file = io_uring_ctx_get_file(ops->ring_fd, false);
|
|
if (IS_ERR(file))
|
|
return PTR_ERR(file);
|
|
ctx = file->private_data;
|
|
|
|
scoped_guard(mutex, &io_bpf_ctrl_mutex) {
|
|
guard(mutex)(&ctx->uring_lock);
|
|
ret = io_install_bpf(ctx, ops);
|
|
}
|
|
|
|
fput(file);
|
|
return ret;
|
|
}
|
|
|
|
static void io_eject_bpf(struct io_ring_ctx *ctx)
|
|
{
|
|
struct io_uring_bpf_ops *ops = ctx->bpf_ops;
|
|
|
|
if (WARN_ON_ONCE(!ops))
|
|
return;
|
|
if (WARN_ON_ONCE(ops->priv != ctx))
|
|
return;
|
|
|
|
ops->priv = NULL;
|
|
ctx->bpf_ops = NULL;
|
|
ctx->loop_step = NULL;
|
|
}
|
|
|
|
static void bpf_io_unreg(void *kdata, struct bpf_link *link)
|
|
{
|
|
struct io_uring_bpf_ops *ops = kdata;
|
|
struct io_ring_ctx *ctx;
|
|
|
|
guard(mutex)(&io_bpf_ctrl_mutex);
|
|
ctx = ops->priv;
|
|
if (ctx) {
|
|
guard(mutex)(&ctx->uring_lock);
|
|
if (WARN_ON_ONCE(ctx->bpf_ops != ops))
|
|
return;
|
|
|
|
io_eject_bpf(ctx);
|
|
}
|
|
}
|
|
|
|
void io_unregister_bpf_ops(struct io_ring_ctx *ctx)
|
|
{
|
|
/*
|
|
* ->bpf_ops is write protected by io_bpf_ctrl_mutex and uring_lock,
|
|
* and read protected by either. Try to avoid taking the global lock
|
|
* for rings that never had any bpf installed.
|
|
*/
|
|
scoped_guard(mutex, &ctx->uring_lock) {
|
|
if (!ctx->bpf_ops)
|
|
return;
|
|
}
|
|
|
|
guard(mutex)(&io_bpf_ctrl_mutex);
|
|
guard(mutex)(&ctx->uring_lock);
|
|
if (ctx->bpf_ops)
|
|
io_eject_bpf(ctx);
|
|
}
|
|
|
|
static struct bpf_struct_ops bpf_ring_ops = {
|
|
.verifier_ops = &bpf_io_verifier_ops,
|
|
.reg = bpf_io_reg,
|
|
.unreg = bpf_io_unreg,
|
|
.check_member = bpf_io_check_member,
|
|
.init_member = bpf_io_init_member,
|
|
.init = bpf_io_init,
|
|
.cfi_stubs = &io_bpf_ops_stubs,
|
|
.name = "io_uring_bpf_ops",
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
static int __init io_uring_bpf_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = register_bpf_struct_ops(&bpf_ring_ops, io_uring_bpf_ops);
|
|
if (ret) {
|
|
pr_err("io_uring: Failed to register struct_ops (%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
__initcall(io_uring_bpf_init);
|