The ublk guide
How Linux ublk works, for anyone writing a ublk server in any language: architecture, control and data planes, every feature flag, and the kernel history behind them.
ublk lets a userspace process implement a Linux block device. The kernel side, ublk_drv, has been in mainline since Linux 6.0 and has grown a feature in almost every release since: user recovery, unprivileged devices, user copy, zoned devices, two generations of zero copy, batch I/O and integrity metadata. The official documentation is a single page in the kernel tree, and the rest of the knowledge lives in the UAPI header, the driver source and the reference servers. This guide collects it in one place.
It is written for people building a ublk server, in C, Rust, Go or anything else that can issue io_uring commands. It assumes you know what a block device is and have at least seen io_uring; it does not assume you have read the driver. Code samples are C-flavored pseudocode against the UAPI structures, because those are what every language ends up marshaling.
Kernel facts here are taken from the upstream header as of Linux 7.3-rc5, the driver source, the kernel’s own ublk documentation, and the reference servers. Where behavior changed between releases, the text says which release, and the kernel version history lists every addition in one table. Where the guide describes go-ublk specifically, it says so; everything else applies to any ublk server.
Read the first three chapters in order. After that, each chapter stands alone.
-
Overview and architecture
What ublk is, how it compares to FUSE, NBD, TCMU and VDUSE, the three device nodes, and one request's path from an application to a ublk server and back.
-
The control plane
Every ublk control command on /dev/ublk-control: encoding, payload, result, errors, and the device state machine they drive.
-
The data plane
Queues, tags, the descriptor mmap, and the FETCH_REQ / COMMIT_AND_FETCH_REQ cycle that moves every request through io_uring.
-
I/O operations and flags
Every UBLK_IO_OP_* a server can receive, the per-request flags, how to report results, and the durability contract behind FLUSH and FUA.
-
Device parameters
struct ublk_params field by field: basic limits, discard, devt, zoned, DMA alignment, segments and integrity, with the kernel's validation rules.
-
Feature flags
All UBLK_F_* feature flags: what each one changes, which release added it, what it requires or excludes, and how negotiation works.
-
Data copy modes and zero copy
How request data reaches the server: the default copy, NEED_GET_DATA, USER_COPY, zero copy with REGISTER_IO_BUF and AUTO_BUF_REG, and shared-memory zero copy.
-
Batch I/O
UBLK_F_BATCH_IO: per-queue PREP, COMMIT and multishot FETCH commands that replace the per-tag protocol.
-
User recovery and quiesce
Keeping /dev/ublkbN alive across a server crash or upgrade: the recovery flags, the QUIESCED and FAIL_IO states, QUIESCE_DEV, and the recovery sequence.
-
Unprivileged devices
UBLK_F_UNPRIVILEGED_DEV: letting a non-root user create and serve ublk devices, the device-path permission check, and the limits that come with it.
-
Zoned devices
UBLK_F_ZONED: exposing a zoned block device, zone operations, REPORT_ZONES and zone append.
-
Integrity metadata
UBLK_F_INTEGRITY: per-request protection information and metadata buffers, the integrity parameters, and how a server reads and writes them.
-
Kernel version history
What each Linux release added to ublk, from 6.0 to 7.3, derived from the UAPI header of every release.
-
Known kernel bugs
Kernel and distribution bugs that bite ublk servers: which builds have them, what they look like, and how to avoid them.
-
Other implementations and resources
Other ublk servers and libraries, the kernel sources and documentation, and where to look when the documentation runs out.