Building and Installing#
Note
rocm-ernic is early-access software that has been tested on limited hardware. It may not work on your system yet.
Requirements#
rocm-ernic builds with GCC or Clang on Linux, and is tested on Ubuntu 24.04. It needs:
CMake 3.21 or later
pkg-config
libvfio-user, built from source (see below), which needs Meson and Ninja to build
libibverbs and librdmacm development packages
GLib 2.0 development package
json-c development package. libvfio-user needs it, and with
ERNIC_BUILD_P4rocm-ernic links it too.
Install them on Ubuntu/Debian:
sudo apt install cmake meson ninja-build pkg-config \
libibverbs-dev librdmacm-dev libglib2.0-dev libjson-c-dev
Build and install libvfio-user if it is not already
available on your system:
git clone https://github.com/nutanix/libvfio-user.git
cd libvfio-user
meson setup build --prefix=/usr
ninja -C build
sudo ninja -C build install
sudo ldconfig
If libvfio-user is installed somewhere other than /usr or
/usr/local, add its pkgconfig directory to
PKG_CONFIG_PATH, or point CMake at the prefix with
-DCMAKE_PREFIX_PATH, e.g.
-DCMAKE_PREFIX_PATH="$HOME/.local".
Compilation#
From the project root directory:
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build
ctest --test-dir build
The executable is produced at build/rocm-ernic. To check it,
start the server with the loopback backend:
./build/rocm-ernic \
--socket /tmp/vfio-user-rocm-ernic.sock \
--backend loopback --verbose
That presents the emulated ionic device (1dd8:1002,
subsystem 1dd8:5400) to any client that connects to the
socket.
Installation#
sudo cmake --install build
By default the binary installs to /usr/local/bin/rocm-ernic,
and its licenses to /usr/local/share/doc/rocm-ernic; a
default install places nothing else.
Override the destination with -DCMAKE_INSTALL_PREFIX=<path>.
Configure with -DERNIC_INSTALL_SERVICE=ON to additionally
install the systemd units, ernicctl, the launcher, the
Prometheus exporter, and the ionic driver patches;
systemd Service and ernicctl lists the full set of installed files. These
files refer to each other by their installed paths, which come
from CMAKE_INSTALL_PREFIX when the project is configured, so
to install them somewhere else, configure with that prefix rather
than passing --prefix to cmake --install. To stage an
install, use DESTDIR.
Nightly Packages#
Every night that develop changes, DEB packages of it for
Ubuntu 24.04 and 26.04 are published as the
nightly prerelease.
They’re development builds for testing, not supported releases.
Download the packages for your Ubuntu release (the file names end
with it) and install them:
sudo apt install ./rocm-ernic_<version>_amd64.deb \
./rocm-ernic-exporter_<version>_all.deb
sudo systemctl daemon-reload
rocm-ernic has the server, with its own copy of
libvfio-user, the systemd service, ernicctl, and the guest
driver pack, installed under /usr. Its configuration is in
/etc/rocm-ernic/rocm-ernic.env (see systemd Service and ernicctl).
rocm-ernic-exporter has the Prometheus exporter, which also
needs prometheus_client (see Prometheus Monitoring and Grafana Dashboard).
Packages#
CPack makes DEB packages of the installed files, one for each install component:
Package |
Contents |
|---|---|
|
The server, and with |
|
The Prometheus exporter, its systemd unit, and the Grafana
dashboard ( |
Ubuntu doesn’t package libvfio-user, and libvfio-user keeps the
soname libvfio-user.so.0 through incompatible changes, so a
package can’t rely on another copy of it. With
-DERNIC_BUNDLE_VFIO_USER=ON, the package has its own copy, in
<libdir>/rocm-ernic, which only the installed server loads:
the server finds it through a DT_RPATH relative to its own
location, which LD_LIBRARY_PATH can’t override. The copy has
to be linked with full RELRO, like the server, which configuring
checks.
The nightly packages are made like this, installed under
/usr:
# libvfio-user, built for the package
cd libvfio-user
LDFLAGS="-Wl,-z,relro,-z,now" meson setup build \
--prefix="$HOME/vfu-pkg" --libdir=lib \
--buildtype=debugoptimized -Ddebug-logs=disabled \
-Dwarning_level=0
meson compile -C build vfio-user
meson install -C build --no-rebuild --tags runtime,devel
# rocm-ernic
cd ../rocm-ernic
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_PREFIX_PATH="$HOME/vfu-pkg" \
-DERNIC_INSTALL_SERVICE=ON \
-DERNIC_BUNDLE_VFIO_USER=ON \
-DERNIC_VFIO_USER_LICENSE="$PWD/../libvfio-user/LICENSE"
cmake --build build
cd build && cpack -G DEB
cpack needs dpkg-shlibdeps, from dpkg-dev, and
file to find the packages’ dependencies. The nightly build
workflow, .github/workflows/nightly.yml, publishes the
packages for each supported Ubuntu release as the nightly
prerelease, once they’ve installed and run in a clean container.
The ernic-package-deb CTest test builds the packages of the
build’s configuration and checks their files, file modes,
dependencies, configuration file, and the paths in the service
files, and, with ERNIC_BUNDLE_VFIO_USER, the server’s
DT_RPATH. It’s registered in a top-level build when
dpkg-dev, file, and readelf are installed. A change to
the install rules has to update its lists of files, in
tests/package/run-package-test.cmake.
Build Options#
Option |
Default |
Description |
|---|---|---|
|
|
Build type (Debug, Release, RelWithDebInfo, etc.). When rocm-ernic is a subproject, the parent project’s build type is used instead. CI builds Debug and Release, so name one of them to match it |
|
|
Build the tests and register them with CTest |
|
|
Treat compiler warnings as errors for project code. Off by default so a build with an unfamiliar compiler is never broken by a new warning; CI turns it on (see Warnings as Errors) |
|
|
Fill every local variable the code doesn’t initialize
with a pattern ( |
|
|
Build with ASan, LSan, UBSan, and the compiler’s other checks that can be combined with them (see Sanitizers) |
|
|
Build with TSan (mutually exclusive with above) |
|
|
Link the shared sanitizer runtime instead of the static one |
|
|
Build with LLVM source-based code coverage; Clang only (see Code Coverage) |
|
|
Run include-what-you-use while compiling; advisory (see include-what-you-use) |
|
|
Run clang-tidy while compiling; every finding is an error. Clang only (see clang-tidy) |
|
|
Build the P4 match-action engine and its tests; needs json-c (see P4 Match-Action Engine) |
|
|
Build Sphinx + Breathe + Doxygen documentation |
|
|
Configure only documentation targets (no library dependencies required) |
|
|
Enable the DKMS targets that build the patched upstream ionic guest modules (see The ionic Device) |
|
|
Linux kernel tag or SHA the ionic sources are fetched
from; must be |
|
kernel.org stable |
Linux kernel git repository the ionic sources are fetched from |
|
|
Also install the systemd units, |
|
|
Install a private copy of libvfio-user that only the installed server uses, for packages (see Packages) |
|
(none) |
libvfio-user’s |
|
|
The release part of the DEB packages’ version, after the
project’s version (see Packages): letters, digits,
|
|
|
Installation prefix |
Warnings as Errors#
ERNIC_WERROR adds -Werror to every target that
ernic_set_compiler_flags() sets up: the server, the test
programs, and the fuzz harness replay programs.
It defaults to OFF so that a packager or downstream
consumer building with a compiler version we have not tested
is never blocked by a newly-introduced warning. That
tolerance is not extended to our own CI: the jobs that build
the project to test or release it configure with
-DERNIC_WERROR=ON, so a new warning fails the build
before it can merge.
Developers should build with it on, matching CI:
cmake -B build -G Ninja -DERNIC_WERROR=ON
Some CI builds leave it off. The CodeQL, clang-tidy, and
include-what-you-use jobs build the project only to analyze
it, and leave checking for warnings to the Build and Test
workflow. The nix static-analysis, dynamic-analysis, and
fuzz builds leave it off because gcc 15 and current clang
emit warnings on the QEMU-ported headers that these builds
cannot suppress. Those lanes are informational and do not
gate merges.
Sanitizers#
ERNIC_USE_SANITIZERS builds the server and the test programs
with every sanitizer except ThreadSanitizer that the compiler
supports and that can be combined with AddressSanitizer:
With GCC and Clang: AddressSanitizer (including LeakSanitizer), UndefinedBehaviorSanitizer, and the
float-divide-by-zerocheck.With GCC: the
bounds-strict,float-cast-overflow,pointer-compare, andpointer-subtractchecks.With Clang: the
implicit-conversion,local-bounds, andnullabilitychecks.
Clang doesn’t get pointer-compare and pointer-subtract,
since in optimized code they report comparisons and subtractions
that the optimizer added.
ERNIC_USE_THREAD_SANITIZER builds them with
ThreadSanitizer. ThreadSanitizer can’t be combined with
AddressSanitizer, so only one of these two options can be on.
Use a separate build directory for each.
Both options need GCC or Clang. Configuring checks that the compiler can build a program with the sanitizers, and fails if, for example, the sanitizer runtimes aren’t installed:
cmake -B build-asan -G Ninja -DCMAKE_C_COMPILER=clang \
-DERNIC_USE_SANITIZERS=ON
cmake --build build-asan
ctest --test-dir build-asan
The sanitizers instrument everything rocm-ernic compiles. Libraries that come from the system, such as libvfio-user, GLib, and libibverbs, are not instrumented.
Every sanitizer report is fatal, so a test with a report fails.
Each program sets some of the sanitizers’ run-time options, such
as the one that turns on GCC’s pointer-compare and
pointer-subtract checks, and the one that makes
ThreadSanitizer stop at its first report (see
cmake/sanitizer-default-options.c). The ASAN_OPTIONS,
UBSAN_OPTIONS, and TSAN_OPTIONS environment variables
override them.
ERNIC_SANITIZER_SHARED_RUNTIME links programs with the
shared library version of the sanitizer runtime. Libraries from
another build that also uses the shared runtime, such as a
sanitizer build of libvfio-user, can then be used in the same
program. If the runtime isn’t in a directory that the dynamic
linker searches, as with Clang’s, the programs get an RPATH to
it. When the option is OFF, programs link the static
runtime. It defaults to what the compiler does on its own:
ON with GCC and OFF with Clang. Ubuntu’s Clang 18
ignores the programs’ default run-time options when they use
its shared runtime.
If ThreadSanitizer stops a program at startup with an
“unexpected memory mapping” error, the kernel’s address space
layout randomization uses more bits than ThreadSanitizer
supports. Run the program under setarch "$(uname -m)" -R,
as CI does, or reduce the randomization with
sudo sysctl vm.mmap_rnd_bits=28.
Code Coverage#
ERNIC_USE_CODE_COVERAGE instruments the server and the
test programs with LLVM source-based coverage. It needs Clang,
and the llvm-profdata and llvm-cov of the same LLVM
version (on Ubuntu, the llvm-<version> package). Each
instrumented program writes its profile to coverage/profraw
in the build directory however it is started, so the servers
that the CTest scripts launch are counted too. The
ernic-coverage target merges the profiles and reports the
coverage of src/:
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang \
-DCMAKE_BUILD_TYPE=Debug -DERNIC_USE_CODE_COVERAGE=ON
cmake --build build
ctest --test-dir build
cmake --build build --target ernic-coverage
The report goes in coverage/report.txt (a summary for each
file) and coverage/lines.txt (the count for each line).
The profiles accumulate across runs; delete
coverage/profraw to start over.
Build Debug, as above: optimized code’s counts don’t match its
source lines as closely. CI’s Build and Test workflow runs one
coverage leg (Clang, Debug, no project-wide sanitizer). It puts
the report on the run’s summary page and fails if line coverage
drops below the floor set in the workflow. On develop it also publishes the
figure to the badges branch, which the README’s Coverage
badge reads.
include-what-you-use#
ERNIC_USE_IWYU runs include-what-you-use (IWYU) on the server’s
and the tests’ sources as they compile, which prints the
#include lines it suggests adding or removing. The
suggestions don’t fail the build, and some are wrong.
IWYU parses with its own clang, which rejects compiler options
that it doesn’t know, so build with the clang that IWYU is
built on. include-what-you-use --version prints its
version:
cmake -B build-iwyu -G Ninja -DCMAKE_C_COMPILER=clang-19 \
-DCMAKE_BUILD_TYPE=Debug -DERNIC_USE_IWYU=ON
cmake --build build-iwyu
The mapping files cmake/iwyu-*.imp correct IWYU where it
names the wrong header. CI’s IWYU workflow lists the files with
suggestions on the run’s summary page, and fails only if IWYU
itself fails. AGENTS.md describes how to run that check
locally and which suggestions to ignore.
clang-tidy#
ERNIC_USE_CLANG_TIDY runs clang-tidy on the server’s
and the tests’ sources as they compile, with the checks in
.clang-tidy. Every finding is an error, so it fails the
build. It needs clang-tidy 19 or later, and Clang as the C
compiler, since clang-tidy rejects the options that the build
uses with GCC. Use the clang of the same version as clang-tidy:
cmake -B build-tidy -G Ninja -DCMAKE_C_COMPILER=clang-23 \
-DERNIC_USE_CLANG_TIDY=ON \
-DERNIC_CLANG_TIDY_EXE=/usr/bin/clang-tidy-23
cmake --build build-tidy -- -k 0
-k 0 keeps building after a file fails, so one build
reports every finding. CI’s clang-tidy workflow builds Debug
and Release with clang-tidy 23 and lists the findings by check
on the run’s summary page.
Guest ionic Modules#
The guest-side driver is the upstream
Linux ionic driver with the patches in
ansible/files/patches/ applied.
Configure with -DERNIC_BUILD_KMOD=ON to get the DKMS
targets, and run them in the guest:
cmake -B build -G Ninja -DERNIC_BUILD_KMOD=ON
cmake --build build --target fetch-ionic-sources
cmake --build build --target build-ionic-dkms
sudo cmake --build build --target install-ionic-dkms
The ionic Device describes the patches, the pinned upstream ref, and how to move to a newer baseline.
Building Documentation#
Documentation requires Doxygen and Python 3. A Python virtual environment is created automatically in the build tree.
cmake -B build -G Ninja -DERNIC_BUILD_DOCS=ON
cmake --build build --target sphinx-html
The generated HTML is written to build/docs/html/.
To build documentation without needing the project’s library dependencies (libvfio-user, glib, libibverbs):
cmake -B build -DERNIC_DOCS_ONLY=ON \
-DERNIC_BUILD_DOCS=ON
cmake --build build --target sphinx-html