Usage#
Basic Usage#
Start the server with a UNIX socket and the desired backend:
# Loopback backend (no external dependencies)
./build/rocm-ernic \
--socket /tmp/vfio-user-rocm-ernic.sock \
--backend loopback
# In-process NVMe-oF controller the guest can connect to
./build/rocm-ernic \
--socket /tmp/vfio-user-rocm-ernic.sock \
--backend nvmeof:size=1G,bs=4096 --tap ernic0
# No backend (minimal stubs)
./build/rocm-ernic \
--socket /tmp/vfio-user-rocm-ernic.sock \
--backend none
# Ethernet attached to a host TAP
./build/rocm-ernic \
--socket /tmp/vfio-user-rocm-ernic.sock \
--backend loopback --tap ernic0
Whoever connects to the socket drives the device as the
guest’s QEMU, so the server makes it mode 0600: only the
user the server runs as, and root, can connect. Run QEMU as
the same user as the server, or as root. To let a QEMU that
runs as another user connect, name a group it is in with
--socket-group (-G); the server gives the socket that
group and makes it 0660 before it serves it. The server
must run as root or be a member of the group:
sudo ./build/rocm-ernic \
--socket /run/vfio-user-rocm-ernic.sock \
--socket-group kvm --backend loopback
Changing the socket’s group by hand after the server starts works too, but the next server started on that path, after a restart or a hot reload, creates a new socket without it.
The server refuses to start if the socket ends up with
different permissions, which a default ACL on its directory
can cause, or with an ACL of its own, which it inherits from a
default ACL with more than the owner, group, and other
entries. An entry naming another user would let that user
connect without the mode showing it. Remove the default ACL
(setfacl -k DIR), or put the socket in another directory.
Put the socket in a directory that only the server’s user and
root can write to, such as /run/rocm-ernic, which the
service uses, or one of your own. Don’t put it in a directory
other users can write to, such as /tmp, even though the
server’s default socket and the examples here are there. The
sticky bit on /tmp stops other users removing or replacing
the server’s socket, but not creating their own at the same
name first, while the server isn’t running. A server that
isn’t root then can’t remove it and fails to start, and a QEMU
that connects to it gives that user the guest’s memory, which
vfio-user shares with whatever serves the device. In a
directory others can write to that isn’t sticky, they can also
replace the server’s socket.
With --socket-group, the group also has to be able to reach
the socket through its directory, which /run/rocm-ernic
allows (mode 0755) and $XDG_RUNTIME_DIR (0700)
doesn’t. A directory of your own can have that group and mode
0710, which lets the group reach the socket but not list or
change the directory:
sudo install -d -o root -g kvm -m 0710 /run/vfio-user
The Emulated Device#
The server emulates an AMD Pensando ionic NIC
(1dd8:1002) with subsystem 1dd8:5400, driven by the
upstream Linux ionic and ionic_rdma modules. Once the
1dd8:5400 entry is published in pci.ids, lspci
names that subsystem ROCm Emulated RDMA NIC after
update-pciids; until then it displays Device [1dd8:5400].
There is nothing to select.
--tap IFNAME (short -T) attaches the emulated
Ethernet interface to an existing host TAP. IFNAME has at
most 15 characters, the Linux limit; the server refuses a
longer one rather than shortening it. Create the TAP up
front, owned by the user running the server:
sudo ip tuntap add dev ernic0 mode tap user "$USER"
The two paths are independent: --backend governs the RDMA
data path, --tap the Ethernet one. See The ionic Device for
the full picture.
nvmeof is the odd one out: rather than carrying guest
RDMA traffic somewhere, it answers it, presenting an NVMe
over Fabrics target the guest connects to with stock
nvme connect -t rdma. See NVMe-oF Controller.
Log Levels#
--log-level (short -L) sets how much the server
prints. It can also be set with the ERNIC_LOG_LEVEL
environment variable; the command line wins when both are
given.
Level |
Output |
|---|---|
|
Nothing at all. |
|
|
|
Default. |
|
Adds the per-operation |
|
Everything, including libvfio-user debug output. |
-v / --verbose remains as shorthand for
--log-level debug.
# Quiet by default -- only warnings and errors
./build/rocm-ernic --backend loopback
# Full per-operation tracing
ERNIC_LOG_LEVEL=info ./build/rocm-ernic --backend loopback
Launching a VM#
Attach the emulated device to a QEMU virtual machine using
the vfio-user-pci transport. QEMU 10.1 or later is
required.
qemu-system-x86_64 \
-machine q35,accel=kvm \
-cpu EPYC \
-smp cpus=2 \
-object memory-backend-memfd,\
id=mem0,share=on,size=2048M \
-machine memory-backend=mem0 \
-nographic \
-drive if=virtio,format=qcow2,\
file=vm-image.qcow2 \
-netdev user,id=net0,\
hostfwd=tcp::2222-:22 \
-device virtio-net-pci,netdev=net0 \
-device '{"driver":"vfio-user-pci",\
"socket":{"path":"/tmp/vfio-user-rocm-ernic.sock",\
"type":"unix"}}'
Inside the guest, load the upstream modules and verify:
sudo modprobe ionic
sudo modprobe ionic_rdma
sudo update-pciids
lspci -nnv -d 1dd8:1002 | grep '\[1dd8:5400\]'
ibv_devices
Statistics Collection#
The server can collect detailed statistics about doorbell rings, WQE processing, and completion queue entries. Statistics are written to a file approximately every second while the server is running.
./build/rocm-ernic \
--socket /tmp/vfio-user-rocm-ernic.sock \
--backend loopback \
--stats-file /tmp/rocm_ernic_stats.txt
# Monitor statistics in real-time
watch -n 0.5 cat /tmp/rocm_ernic_stats.txt
The statistics file includes:
Device-level statistics (commands, register reads/writes, UAR writes, interrupts)
Per-QP statistics:
Doorbell rings (send, receive, SRQ)
WQEs processed (total and by opcode type)
CQEs posted
Continuation callbacks scheduled
Statistics are automatically written on server exit
(SIGINT / SIGTERM) in addition to the periodic
updates.
Each update is written to a temporary file next to the stats
file (its name with six random characters appended), which
then replaces the stats file, so a reader always sees one
whole update. The server therefore needs write permission on
the stats file’s directory. The file is mode 0644
whatever the umask. If an update can’t be written, the server
logs why and leaves the previous file in place.
Development Workflow: Hot Reload#
During development you often need to rebuild the server and test changes against a running VM. The hot-reload workflow lets you do this without tearing down and rebooting the VM, cutting the typical iteration cycle from 60+ seconds down to roughly 5–15 seconds (depending on build time).
The recommended approach is the ernicctl service
CLI, which manages server instances, VMs, QMP sockets,
and driver distribution through a single tool. See
systemd Service and ernicctl for full documentation.
Quick example using ernicctl#
# Start the service (launches all instances)
sudo systemctl start rocm-ernic
# Launch a VM for instance 1
sudo ernicctl vm-launch 1
# After making code changes, hot-reload instance 1
sudo ernicctl hot-reload 1 --update-driver
Standalone hot-reload script#
The scripts/hot-reload.sh script is also available
for standalone use outside the systemd service. It
uses QEMU’s QMP (QEMU Machine Protocol) to hot-unplug
the vfio-user-pci device, restart the server, and
hot-plug a fresh device – all while the VM keeps
running.
Host dependency: socat is required for QMP
communication.
sudo apt install socat
./scripts/hot-reload.sh
Useful options:
--no-build– skip the rebuild step--build-only– rebuild without cycling the device--update-driver– rebuild and reload the guest kernel module--backend TYPE– choose a different backend (default:loopback)