2026-07-10
Regular io_uring already cuts syscall overhead by batching submissions: you fill the submission queue (SQ) in shared memory, then call io_uring_enter() once to tell the kernel to consume them. But that single syscall still costs ~500ns and blows out your L1 with kernel code. SQPOLL mode eliminates it entirely — a dedicated kernel thread polls the SQ ring and dispatches I/O as soon as entries appear.
You enable it by setting IORING_SETUP_SQPOLL in io_uring_setup(). The kernel spawns a thread (visible as iou-sqp-<pid>) that spins on the SQ tail pointer. Your userspace code just writes an SQE, advances the tail with a release store, and moves on — no syscall, no context switch, no ring transition. The submission-to-dispatch latency drops from microseconds to tens of nanoseconds.
The catch: a spinning kernel thread burns a whole core. To avoid this, SQPOLL parks itself after sq_thread_idle milliseconds of inactivity (default 1ms, tunable). When parked, the IORING_SQ_NEED_WAKEUP flag appears in the SQ ring's flags field. Your submission fast-path must check it:
NEED_WAKEUP is set, call io_uring_enter() with IORING_ENTER_SQ_WAKEUP to nudge the poller.Real-world example: ScyllaDB's Seastar framework uses SQPOLL for its storage engine on dedicated I/O cores. On an NVMe workload doing 1M IOPS per core, the syscall overhead of vanilla io_uring (~500ns × 1M = 500ms/sec = 50% CPU) disappears entirely. They pin the SQPOLL thread with IORING_SETUP_SQ_AFF to a sibling hyperthread so the polling thread shares the L2 with the submitting thread — SQE writes stay hot in cache.
Rule of thumb: SQPOLL wins when your submission rate is high enough that the polling core is genuinely earning its keep. Break-even is roughly 1 submission per microsecond — below that, a busy core burning 100% to save 500ns per call is a bad trade. Above that, and especially above 100k IOPS/core, SQPOLL becomes essentially free per-op.
Since kernel 5.13, unprivileged users can enable SQPOLL (it used to require CAP_SYS_NICE). And since 5.11, multiple io_uring instances can share one SQPOLL thread via IORING_SETUP_ATTACH_WQ, so a 16-core server doesn't need 16 pollers.
