
Linux 7.0 landed on April 12 and every headline went straight to Rust. Understandable — Rust going stable in the kernel is a big deal. But buried in the same release is something more immediately actionable: io_uring just got BPF filtering, and it changes the performance calculus for every team that disabled it for security reasons. That’s most teams.
The Problem io_uring Has Had Since 2022
If you’ve been following io_uring’s trajectory, you know the awkward history. Google reported that 60% of kernel exploits submitted to their kCTF bug bounty in 2022 targeted io_uring. The response was decisive: Google disabled it across GKE Autopilot, ChromeOS, and production servers. Docker’s default seccomp profile blocked it entirely. The reasoning was sound — io_uring’s complexity created a large attack surface and there was no way to restrict which operations a process could actually use. The choice was binary: enable io_uring and accept full exposure, or block the syscall entirely and give up the performance gains.
That binary choice is now gone.
BPF Filtering: What Linux 7.0 Actually Adds
The new feature, implemented by Jens Axboe (io_uring’s original author), adds a registration operation called IORING_REGISTER_BPF_FILTER. It lets you attach a classic BPF program to a specific io_uring operation code. The program is invoked after initial SQE preparation — the kernel has already copied the request structures in, so the filter sees the actual operation parameters. Return non-zero to allow the request. Return zero to block it.
Multiple filters can be stacked per opcode. You can allow IORING_OP_READ and IORING_OP_WRITE while blocking operations your workload doesn’t need. This is the same model as seccomp — and it uses classic BPF rather than eBPF for exactly the same reason seccomp does: classic BPF programs work in container contexts without elevated privileges.
The immediate implication: container runtimes and systemd can now attach io_uring filters that are actually meaningful. Previously, tools like Docker couldn’t filter io_uring operations because the relevant data lived in the submission queue ring, out of band from where seccomp operates. That gap is closed. As LWN noted when tracking this feature’s development: “fine-grained control may make io_uring available in contexts where it has been blocked.”
Zero-Copy Receive: The Throughput Number Worth Your Attention
Merged in 6.15 and fully operational in 7.0, the zero-copy receive feature (IORING_OP_RECV_ZC) eliminates the kernel-to-user memory copy on the network receive path. Instead of copying packet data from kernel buffers to userspace, the kernel configures a page pool that the hardware RX queue fills directly with user pages. Reading a socket effectively becomes a notification: the kernel tells userspace where the data is, not what it is.
| Method | Throughput | vs epoll |
|---|---|---|
| epoll | 66.9 Gbps | baseline |
| io_uring ZC (4096 buf) | 92.2 Gbps | +37.8% |
| io_uring ZC (high load) | 116.2 Gbps | +41.4% |
To be direct about scope: this matters for load balancers, high-frequency trading systems, real-time data pipelines, and anything running on 10GbE+ links that’s actually I/O-saturated. You need network driver support, a 6.15+ kernel, and a workload that can exploit large receive batches. The kernel documentation for io_uring zero-copy Rx covers driver requirements in detail.
IOPOLL Gets Smarter — Especially for Database Servers
The third io_uring change in 7.0 is quieter but has concrete production impact. IOPOLL — io_uring’s polling mode for NVMe devices — previously tracked pending requests in a singly linked list. Individual items couldn’t be removed easily. When different devices were polled in the same ring, completion of finished requests got deferred unnecessarily.
Linux 7.0 upgrades IOPOLL to hash-table tracking, so the scheduler completes whichever requests finished without waiting for earlier ones in sequence. Bytedance engineers benchmarked this and confirmed measurable improvement in polled-mode workloads.
The most actionable real-world application: PostgreSQL 18 with io_method = io_uring. PostgreSQL 18 shipped io_uring support built in. On I/O-intensive workloads — a 300GB dataset on 64GB RAM — benchmarks showed up to 3x improvement over synchronous I/O. The IOPOLL fix in 7.0 makes this path even more reliable under mixed-device configurations.
What to Do Now
- Check your kernel version. Run
uname -r. You need Linux 7.0+ for BPF filtering and IOPOLL fixes. Linux 6.15+ for zero-copy receive. Ubuntu 26.04 LTS ships 7.0; older LTS releases need the HWE kernel stack or a manual upgrade. - PostgreSQL 18 users: enable io_uring. Add
io_method = io_uringto postgresql.conf. Official packages include liburing support. Benchmark your specific workload before rolling to production — gains are real but workload-dependent. - Container operators: revisit your seccomp policies. The old “block io_uring entirely” guidance is now a performance liability. With BPF filtering on Linux 7.0, you can write an allowlist for the io_uring ops your workload actually uses. Worth a conversation with your security team.
- Rust developers: If you’re building a new high-throughput network service targeting Linux, evaluate io_uring-backed runtimes. ByteDance’s monoio runs in production at scale; tokio-uring is available for Tokio users.
The Bigger Picture
io_uring has been a “bleeding edge” story for five years. Linux 7.0 marks a turning point. The security objection — real, not theoretical — now has a proportionate answer in BPF filtering. PostgreSQL, Cloudflare, .NET, and Apache Iggy are already running io_uring in production. Ubuntu 26.04 LTS ships Linux 7.0, which means this kernel is coming to your production environment whether or not you actively seek it out.
Teams that benchmark now and build familiarity with liburing or the PostgreSQL io_uring path will have a concrete advantage over teams that treat this as future work. The feature is stable, the tooling exists, and the security excuse has an answer. Time to act.













