Skip to content

io_uring support #690

Open
Open

Description

@Geal

we need to plan for io_uring integration. It is a new Linux syscall that allows sending a queue of syscalls to execute. The kernel will then execute those calls (if possible) and return the results in another queue.
This works a bit like IO completion ports on Windows. This design gives great performance gains, since for a serie of syscalls, we will not need to call from userland to kernel repeatedly, but just call once and let the kernel perform all of the actions.
Submissions to the queue can be linked, so that they can be cancelled if one of them fails.

Example:

  • epoll notifies us that the front socket of a TCP proxy session is readable
  • we submit a read from the front socket in a buffer, and a linked write to the back socket
  • the kernel will execute them in order and copy data from one socket to the next, without userland interaction, like splice would do

we can submit a lot of unrelated tasks, that may not be linked and can complete at various points in the future. There's a limit to the number of tasks though, and this will not replace the epoll based event loop. When we submit a read or write task using a buffer, that buffer must not be used from userland in the meantime.

Here's how it could work in practice:

  • epoll returns events indicating which sessions can advance
  • we call those sessions with the completion queue as argument
  • sessions submit tasks (read from the front socket, connect to a backend server, etc)
  • when sessions are done submitting or the queue is full, we send it to the kernel
  • we look at the completion queue to see which operations finished and which failed
  • we notify the affected sessions, they return a result indicating if they have more work to do

This will require more separation between the IO part and the protocol part of sessions (which is already started with the readable_parse() method for HTTP), where if we want to do some IO, either we submit an operation and pass in "waiting for IO part" with io_uring, or do it directly with basic APIs, then when the result comes go to the next step where we handle the data.

Example usage:

Activity

  1. added this to the next milestone on Feb 18, 2022
  2. ccarral commented on Jul 5, 2023

    @ccarral

    Hi, I am interested in contributing to this project and this issue caught my attention.

    The way I see it from reading the issue, the polling for Interest::READABLE and Interest::WRITABLE events is still going to be handled by mio, so that leaves us with the following syscalls to be handled by uring:

    • accept()
    • connect()
    • read()
    • write()

    But how would the linked read() -> write() know how many bytes were read/are to be written in the backend socket? Maybe I am missing something.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions