Io_uring ignoring the timeout set to the io_uring.enter method

I'm using io_uring v 0.7.8 (latest), and I'm not able to set a timeout to the 'enter' method on the 'ring.submitter()' (at the moment I prefer to use this method and not to push a timeout sqe).

  1. First I checked the extended arguments are supported ... and they are on my 6.12 kernel.
if !params.is_feature_ext_arg() {  
   panic(bla bla bla)
}
  1. I built the struct 'io_uring_getevents_arg':
#[repr(C)]
struct GeteventsArg {
    sigmask:    u64,              // always 0
    sigmask_sz: u32,              // always 0
    pad:        u32,              // always 0
    ts:         u64,              // pointer to Timespec
}
  1. I prepared my timeout:
use io_uring::types::Timespec;
let secs = write_timeout_ms / 1_000;
let nsec = (write_timeout_ms % 1_000) * 1_000_000;
let ts = Timespec::new().sec(secs as u64).nsec(nsec as u32);
  1. I instantiated that GeteventsArg:
let arg = GeteventsArg {
    sigmask:    0,
    sigmask_sz: 0,
    pad:        0,
    ts: (&ts as *const Timespec) as u64,
};
  1. I called io_uring enter with the required flags and using 3 for to_submit and min_complete parameters, because I always send these sqes: open->write->close, where open is linked to writeFixed and writeFixed is linked to close, and close does not link to anyone. I had to declare these flags on my own code because I did not find a way to use them from any public api.
const IORING_ENTER_SQ_WAIT:   u32 = 1 << 1;
const IORING_ENTER_GETEVENTS: u32 = 1 << 0;
const IORING_ENTER_EXT_ARG:   u32 = 1 << 2;

let flags = IORING_ENTER_SQ_WAIT | IORING_ENTER_GETEVENTS | IORING_ENTER_EXT_ARG;
let ret = unsafe {
    ring.submitter().enter(
        3,      // to_submit
        3,      // min_complete
        flags,
        Some(&arg),
    )
};
  1. And finally I processed the SQEs:
// Process all completions, if there is an error continue processing until completely drain the 'completion' queue.
let mut maybe_err = None;  
for cqe in ring.completion().take(3) {
    let res = cqe.result();
    if res < 0 && maybe_err.is_none() {
       maybe_err = Some(Error::from_raw_os_error(-res));
    }
}
maybe_err.map_or(Ok(()), Err)

This code works like a charm, and the 32MB files are successfully written to disk, but the timeout is absolutely ignored. I tried with a timeout of just 1 nanosecond, and continue writing to disk, which is impossible in just 1ns.

Can anyone give me some light about what I'm doing wrong?

1 Like

the timeout you are setting is the blocking timeout which means, how much are we willing to wait for that syscall in a blocking state, it doesnt enfore the timeout on the io operation itself, also about the speed of writing 32mb to disk,buffered writes complete instantly(without blocking) in the kernel , as the kernel actually just copies the data to its page cache and marks it as completed and the actual disk write happen later, so basically if you want to really hit your 'blocking timeout' try maybe writing to an empty pipe and it will block when it's full.

Thanks for the quick response, If I understand well, if I set up a 1s timeout, that means our app will wait 1s at most for the io operation to finish, and this timeout will be thrown, but at kernel level the write operation will continue. Is that true?

Yes, to be exact, your application thread will block for at most 1 second waiting for completions.
This kind of timeout is used to make sure that your thread doesn't block forever.