Most storage hardware projects start with a datasheet and end, months later, with a
driver that either works or doesn't. OpenFlash Controller Lab —
an open-source repository from developer manishklach — takes a
different route. It builds the entire controller stack as an executable
architecture first: a discrete-event NAND and flash-translation-layer simulator,
a behavioral reference controller, a versioned host/controller ABI, a QEMU PCI device
model, and a Linux blk-mq driver scaffold — all wired together so that
scheduling and placement ideas can be measured before anyone commits to RTL.
The premise is simple but easy to skip in practice: NAND media is slow relative to PCIe and the CPU, and the only way a controller hides that latency is by keeping many independent channels and dies busy at once. That single fact cascades into requirements for every layer of the stack — the FTL has to place data without triggering pathological garbage collection, the scheduler has to exploit parallelism without starving reads or writes, the queue transport has to avoid stale completion races, and the Linux driver has to submit asynchronously and recover cleanly from timeouts. OpenFlash's whole design is an attempt to turn that chain of requirements into code you can actually run.
01 What's already running
The project is unusually candid about where it stands, and that candor is worth taking seriously — the README states outright that it is not yet suitable for storing valuable data or loading against unverified hardware. Here's the shape of what does work today:
02 How the pieces fit together
The whole point of the project is that these components aren't isolated demos — they're a single pipeline running from an application request down to a simulated die. Traffic flows through the same stages a shipped SSD would use:
This is deliberately a managed block device architecture: FTL, ECC,
wear-leveling, and bad-block policy stay behind the controller ABI, and Linux talks to
it through blk-mq like any other NVMe-class device. A raw-NAND
MTD/UBI/UBIFS design is acknowledged as possible but treated as an explicitly separate
product boundary, rather than something bolted onto the same ABI.
03 Numbers you can actually reproduce
Because the simulator is seeded, the same command produces the same result on any machine — which is what makes it useful for comparing scheduling policies rather than just admiring them. Running the built-in comparison between FIFO and read-priority scheduling looks like this:
# 10,000 requests, 8 channels, 4 dies/channel, 70% reads python -m openflash.cli compare \ --requests 10000 --read-ratio 0.70 \ --channels 8 --dies-per-channel 4 --seed 7 fifo 644.5 MiB/s 41249 IOPS p99=211871.6 us read-priority 817.6 MiB/s 52328 IOPS p99=167508.5 us
Read-priority scheduling wins here on every axis in this particular configuration — more throughput, more IOPS, and a much tighter p99 read tail. The project is careful to flag that these are model results, not hardware benchmarks, which is exactly the right instinct: a simulator can tell you which scheduling idea is directionally promising, but it can't tell you what silicon will actually do until it's calibrated against a real NAND part.
04 Four things worth knowing before you clone it
The ABI is the contract
A 64-byte little-endian command/completion layout, BAR0 lifecycle, and durability rules are defined once in a normative C header, then checked for consistency against the Python and QEMU implementations in CI.
The driver is intentionally incomplete
It binds to PCI, sets up admin rings, and completes identify — but does not register a disk yet. Timeout/reset semantics and data verification have to land first.
Licensing is split correctly
The simulator, docs, and QEMU model are Apache-2.0. The Linux kernel driver subtree is GPL-2.0-only, isolated because it depends on GPL-only kernel interfaces.
fio profiles exist, but are future gates
The bench/fio profiles describe the acceptance workloads the project
wants to pass once a real block device is registered — they aren't runnable results yet.
05 What's next on the roadmap
The project lays out its own path in stages, and stage two — turning the emulated controller into a working Linux data path — is where the near-term work concentrates:
- Add QEMU QTests for MMIO, DMA, ring wraparound, malformed commands, reset, and MSI-X.
- Generalize the working identify path into asynchronous admin completion handling.
- Negotiate multiple I/O queues and map MSI-X vectors to per-queue completion handling.
- Register a
blk-mqdisk with read/write/flush/FUA/discard and timeout/reset behavior. - Run fio verification and fault injection before any persistent image backing is enabled.
Beyond that sits the more ambitious stage three: moving the validated scheduler and FTL behavior into firmware, building ONFI channel engines, persistent metadata recovery, FPGA tests, and eventually power-cut qualification. It's a long runway, but it's the right order — validate the policy in software, then let the hardware inherit behavior that's already been tested, rather than the other way around.
What makes OpenFlash worth a look isn't that it's a finished SSD controller — it explicitly isn't one yet. It's that the project treats "is this architecture idea any good" as a question you should be able to answer with a seeded simulation and a test suite, months before you'd ever tape out silicon. That's a discipline a lot of storage hardware projects skip, and it's the kind of foundation that makes the eventual firmware and RTL work far less risky.
The full source, docs, and roadmap gates live on GitHub.
View openflash-controller-lab →