This topic describes how to use the io_uring percpu sqthread feature and handle common error codes.
Background
io_uring is a high-performance asynchronous I/O interface in the Linux kernel that uses a pair of shared memory buffers (ring buffers) to implement efficient communication between applications and the kernel, thereby improving performance and efficiency. sqpoll is an io_uring feature that enables a separate kernel thread to process tasks in the submission queue (SQ). The thread is called sqthread.
If multiple io_uring instances are created in the same process with sqpoll enabled, all sqthreads may run on the same CPU core. Each io_uring instance starts a separate sqthread. As such, multiple sqthreads simultaneously run on a single core, and sqthread context switching incurs unnecessary overhead. To implement efficient sqpoll functionality and reduce usage complexity, Alibaba Cloud Linux 3 provides the io_uring percpu sqthread feature, which allows you to specify the core on which an sqthread runs during thread creation. Additionally, only one sqthread can exist on each core, and this sqthread polls and processes requests from all io_uring instances bound to it.
Limits
Operating system: Alibaba Cloud Linux 3.
Kernel version: 5.10.112-11 or later.
Use the percpu sqthread feature
Enable the percpu sqthread feature
When initializing io_uring, set the IORING_SETUP_SQPOLL flag to enable the sqpoll mode. To enable the percpu sqthread feature, configure IORING_SETUP_SQPOLL_PERCPU and IORING_SETUP_SQ_AFF and specify the ordinal number of the CPU where the sqthread is located for sq_thread_cpu. Sample code:
struct io_uring ring;
struct io_uring_params params = {};
params.flags = IORING_SETUP_SQPOLL|IORING_SETUP_SQPOLL_PERCPU|IORING_SETUP_SQ_AFF;
params.sq_thread_cpu = 1;
ret = io_uring_queue_init_params(ENTRIES, &ring, ¶ms);
if (ret < 0) {
perror("io_uring_queue_init_params");
exit(EXIT_FAILURE);
}Submit and process I/O requests
Use the
io_uring_sqestruct to prepare I/O requests.struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); if (!sqe) { fprintf(stderr, "get sqe failed\n"); return -1; } io_uring_prep_read(sqe, fd, buf, len, offset); sqe->user_data = (uint64_t)buf;Use the
io_uring_submitfunction to submit requests to the SQ.ret = io_uring_submit(&ring); if (ret < 0) { perror("io_uring_submit"); return -1; }Use the
io_uring_wait_cqeorio_uring_peek_cqefunction to retrieve completed I/O requests from the completion queue (CQ).struct io_uring_cqe *cqe; ret = io_uring_wait_cqe(&ring, &cqe); if (ret < 0) { perror("io_uring_wait_cqe"); return -1; } if (cqe->res < 0) { fprintf(stderr, "read error: %s\n", strerror(-cqe->res)); } else { printf("read %d bytes\n", cqe->res); } io_uring_cqe_seen(&ring, cqe);