add timeouts for io_uring bpf-loop
Subsystems (2)
Pavel Begunkov
maintainercover add timeouts for io_uring bpf-loop
The BPF ops should be able to set timeout while waiting for CQEs, this series implements one approach. I'm keeping it as an RFC as I'm pondering at a slightly different and hopefully more performant ways of achieving it.
Pavel Begunkov (2):
io_uring/loop: add a structure for loop state
io_uring/loop: introduce wait timeouts
io_uring/bpf-ops.c | 6 ++++++ io_uring/loop.c | 37 +++++++++++++++++++++++++++++++------ io_uring/loop.h | 14 ++++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-)
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerRFC 1/2 io_uring/loop: add a structure for loop state
struct iou_loop_params is BPF accessible, but I'll need to keep extra state that BPF programs shouldn't see. Add a new structure, which is just wrapping around the params for now but will be extended later.
--- io_uring/loop.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-)
io_uring/loop.c +11 -5
@@ -3,6 +3,10 @@#include "wait.h" #include "loop.h" +struct io_loop_state { +struct iou_loop_params lp; +}; +static inline int io_loop_nr_cqes (const struct io_ring_ctx *ctx,const struct iou_loop_params *lp) {
@@ -21,9 +25,11 @@ static inline void io_loop_wait_finish(struct io_ring_ctx *ctx)atomic_set (&ctx->cq_wait_nr,IO_CQ_WAKE_INIT ); } -static void io_loop_wait (struct io_ring_ctx *ctx,struct iou_loop_params *lp, +static void io_loop_wait (struct io_ring_ctx *ctx,struct io_loop_state *ls,unsigned nr_wait) { +struct iou_loop_params *lp = &ls->lp; +io_loop_wait_start (ctx, nr_wait); if (unlikely(io_local_work_pending(ctx) ||
@@ -41,7 +47,7 @@ static void io_loop_wait(struct io_ring_ctx *ctx, struct iou_loop_params *lp,static int __io_run_loop (struct io_ring_ctx *ctx) { -struct iou_loop_params lp = {}; +struct io_loop_state ls = {};while (true) {int nr_wait, step_res;
@@ -49,15 +55,15 @@ static int __io_run_loop(struct io_ring_ctx *ctx)if (unlikely (!ctx->loop_step))return -EFAULT ; - step_res = ctx->loop_step (io_loop_mangle_ctx (ctx), &lp); + step_res = ctx->loop_step (io_loop_mangle_ctx (ctx), &ls.lp);if (step_res ==IOU_LOOP_STOP )break ;if (step_res !=IOU_LOOP_CONTINUE )return -EINVAL ; - nr_wait =io_loop_nr_cqes (ctx, &lp); + nr_wait =io_loop_nr_cqes (ctx, &ls.lp);if (nr_wait >0 ) -io_loop_wait (ctx, &lp, nr_wait); +io_loop_wait (ctx, &ls, nr_wait);else nr_wait =0 ;
Signature · 1 line
2.54.0
Pavel Begunkov
maintainerRFC 2/2 io_uring/loop: introduce wait timeouts
In reply to Pavel BegunkovThe basic waiting functionality should be able to time out of CQE waiting. Implement timeouts for the loop logic and let BPF to control it. The BPF API consists of two flags it can set in struct iou_loop_params, IOU_LOOP_TIMEOUT[_ABS], which instruct the loop to arm a timeout while waiting using the value passed in struct iou_loop_params::timeout_ns as either a relative of absolute timeout. It can also find out if the last waiting timed out by checking for the IOU_LOOP_WAIT_TIMED_OUT flag.
--- io_uring/bpf-ops.c | 6 ++++++ io_uring/loop.c | 21 ++++++++++++++++++++- io_uring/loop.h | 14 ++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-)
io_uring/bpf-ops.c +6 -0
@@ -95,6 +95,12 @@ static int bpf_io_btf_struct_access(struct bpf_verifier_log *log,if (t == loop_params_type) { if (off + size <= offsetofend(struct iou_loop_params, cq_wait_idx))return SCALAR_VALUE ; +if (off >= offsetof(struct iou_loop_params , timeout_ns) && + off + size <= offsetofend(struct iou_loop_params, timeout_ns)) +return SCALAR_VALUE ; +if (off >= offsetof(struct iou_loop_params , flags) && + off + size <= offsetofend(struct iou_loop_params, flags)) +return SCALAR_VALUE ; }return -EACCES ;
io_uring/loop.c +20 -1
@@ -5,6 +5,7 @@struct io_loop_state {struct iou_loop_params lp; +__u32 cur_flags; }; static inline int io_loop_nr_cqes(const struct io_ring_ctx *ctx,
@@ -40,7 +41,20 @@ static void io_loop_wait(struct io_ring_ctx *ctx, struct io_loop_state *ls, }mutex_unlock (&ctx->uring_lock); -schedule (); + +if (ls->cur_flags &IOU_LOOP_TIMEOUT ) { +ktime_t timeout =ns_to_ktime (lp->timeout_ns); +enum hrtimer_mode mode =HRTIMER_MODE_REL ; + +if (ls->cur_flags &IOU_LOOP_TIMEOUT_ABS ) + mode =HRTIMER_MODE_ABS ; + +if (!schedule_hrtimeout_range_clock (&timeout,0 , mode,ctx->clockid)) + ls->lp.flags |=IOU_LOOP_WAIT_TIMED_OUT ; + }else { +schedule (); + } +io_loop_wait_finish (ctx);mutex_lock (&ctx->uring_lock); }
@@ -60,6 +74,11 @@ static int __io_run_loop(struct io_ring_ctx *ctx)break ;if (step_res !=IOU_LOOP_CONTINUE )return -EINVAL ; +if (ls.lp.flags & ~IOU_LOOP_VALID_FLAGS ) +return -EINVAL ; + + ls.cur_flags = ls.lp.flags; + ls.lp.flags =0 ; nr_wait =io_loop_nr_cqes (ctx, &ls.lp);if (nr_wait >0 )
io_uring/loop.h +14 -0
@@ -4,12 +4,26 @@#include <linux/io_uring_types.h> +enum iou_loop_flags { +IOU_LOOP_TIMEOUT =1U <<0 , +IOU_LOOP_TIMEOUT_ABS =1U <<1 , +/* If set, the last loop waiting was interrupted by a timeout */ +IOU_LOOP_WAIT_TIMED_OUT =1U <<2 , +}; + +#define IOU_LOOP_VALID_FLAGS (IOU_LOOP_TIMEOUT |\ + IOU_LOOP_TIMEOUT_ABS |\ + IOU_LOOP_WAIT_TIMED_OUT) +struct iou_loop_params {/* * The CQE index to wait for. Only serves as a hint and can still be * woken up earlier. */ __u32 cq_wait_idx; +/* see IOU_LOOP_* flags */ +__u32 flags; +__u64 timeout_ns; };enum {
Signature · 1 line
2.54.0