NoSSD
This page is hw/femu/docs/modes/nossd.md at FEMU 39a55eeb6 (2026-10-02), licensed GPL-2.0-or-later. Send corrections to the FEMU repository.
NoSSD mode (femu_mode=2, the default) emulates an NVMe drive with no media
model. Reads and writes copy data between guest memory and a host DRAM
buffer and complete as soon as the poller finishes the copy. There is no
FTL, no GC and no NAND time.
Use it when the storage device should not be the bottleneck: work on the host I/O stack, the NVMe driver, polling, SPDK or io_uring, or as an upper bound to compare a timed mode against. For a device with SSD latency and garbage collection, use BlackBox.
Requirements
- Host and guest: see the mode table and requirements.md. Any guest kernel with the NVMe driver works.
- Host memory:
devsz_mbplus guest RAM plus about 1 GiB for QEMU, about 9 GiB forrun-nossd.sh(a 4 GiB device and a 4 GiB guest). - Host cores: the pollers spin on host cores. For throughput work, give each
femu-pollerthread a core of its own.
Launch
From build-femu/:
./run-nossd.sh
The FEMU device in that script is:
-device femu,devsz_mb=4096,id=nvme0
femu_mode is not set, so it is 2. Writing femu_mode=2 has the same
effect.
Configuration
NoSSD ignores the NAND geometry, timing and FTL properties. These are the ones that change its behaviour.
Capacity and namespaces
Properties: mode, capacity and namespaces.
devsz_mb is the namespace size. namespaces, namespace_sizes and
namespace_modes split it into several namespaces
(multi-namespace guide).
Queues and pollers
Properties: queues, pollers and interrupts.
queuessets the number of I/O queue pairs (default 8).- By default one poller thread serves every I/O queue. With
multipoller_enabled=1, FEMU runsceil(queues / poller_ratio)pollers, each owning a round-robin share of the queues:
-device femu,devsz_mb=4096,queues=8,multipoller_enabled=1,poller_ratio=2
This starts four pollers. Give each one a host core.
Host link and controller firmware
Properties: host link and controller firmware.
pcie_bandwidth_mbps, pcie_prop_delay_ns and fw_cpu_ns add link and
firmware time to each Read and Write. With any of them set, NoSSD completes
commands through the same timed queue as the other modes instead of in the
poller sweep, which adds work per command.
-device femu,devsz_mb=4096,pcie_bandwidth_mbps=3500,pcie_prop_delay_ns=1000
Optional commands and features
Properties: controller identity and capabilities, LBA formats, metadata and protection.
oncsturns on optional NVM commands. The default (0x14) offers Dataset Management and Save/Select. Add 0x8 for Write Zeroes, 0x1 for Compare, 0x100 for Copy.- NoSSD supports namespace management, metadata and protection information
and Streams (
streams=on, which tracks streams but places nothing).
Use it from the guest
Check the device:
sudo nvme list
sudo nvme id-ctrl /dev/nvme0 | grep -E '^(mn|sn) '
The model is FEMU NoSSD NVMe Controller and the serial number starts with
vNoSSD.
Measure latency and throughput with fio. Unlike BlackBox, unwritten blocks read at the same speed as written ones:
sudo fio --name=lat --filename=/dev/nvme0n1 --direct=1 --ioengine=io_uring \
--rw=randread --bs=4k --iodepth=1 --runtime=30 --time_based
sudo fio --name=tput --filename=/dev/nvme0n1 --direct=1 --ioengine=io_uring \
--rw=randread --bs=4k --iodepth=64 --numjobs=4 --group_reporting \
--runtime=30 --time_based
The SMART log counts host reads and writes in every mode, NoSSD included:
sudo nvme smart-log /dev/nvme0
The vendor log page C0h stays zero: NoSSD has no media to count.
Limits and refusals
| Message | Cause and fix |
|---|---|
multipoller_enabled must be 0 (one poller for all queues) or 1 (each poller owns poller_ratio queues) | Any other value. |
queues must be in [1, 2047] | queues out of range. |
oncs may only set Compare, Write Uncorrectable, DSM, Write Zeroes, Save/Select Feature Support, Verify and Copy | A bit FEMU does not implement. |
If the host cannot allocate devsz_mb of memory, QEMU aborts in GLib with
failed to allocate N bytes.
Verify
sudo nvme listshows the modelFEMU NoSSD NVMe Controller.- A queue depth 1 random read reports a completion latency of a few microseconds to tens of microseconds, depending on the host, with no difference between written and unwritten blocks.
sudo nvme smart-log /dev/nvme0shows the read and write command counts moving.
Troubleshooting
- Throughput does not grow with more guest jobs. A single poller serves
every queue by default. Set
multipoller_enabled=1with apoller_ratiothat leaves one poller per host core you can spare, and pin the threads. - Latency varies from run to run. The pollers compete with vCPUs and other host work. Pin vCPUs and pollers to separate cores, and keep the host CPU at a fixed frequency (see performance tuning).
- Data is gone after QEMU exits. FEMU keeps the device only in host memory. A guest reboot keeps the data.
Related issues: #52, #69.