feat(build): put virtio-nvgpu's Vulkan layer into the guest rootfs (#356)

On an NVIDIA host, NVIDIA's Vulkan driver in the guest lists ray
tracing, optical flow, binary import and CUDA kernel launch, then fails
`vkCreateDevice` with `VK_ERROR_INITIALIZATION_FAILED` for each of them.
An engine that enables what it is offered does not start. virtio-nvgpu's
guest layer (`vklayer/`, nestrilabs/virtio-nvgpu#5) hides those
extensions. It acts only on an NVIDIA device in a guest that has the
virtio-nvgpu driver, so on AMD and Intel boxes it loads and does
nothing.

- A new `nvgpu-layer-build` stage builds the layer from virtio-nvgpu at
`NVGPU_REF`, the same setting the kernel build uses for the guest
driver. The commit is recorded in `/usr/share/nestri/virtio-nvgpu.rev`.
- The layer and its manifest go in `/usr/lib` and
`/usr/share/vulkan/implicit_layer.d`. The missing-library check covers
the layer.

**Merge after nestrilabs/virtio-nvgpu#5.** The default `NVGPU_REF=dev`
has no `vklayer/` until that PR lands, so `make build` fails at this
stage before then.

Built on nestripc-1 with `NVGPU_REF=feat/v0.2-compute` (virtio-nvgpu
112eac4) and pushed:
- `ghcr.io/nestrilabs/nestri/base:latest` sha256:152a4485…
- `ghcr.io/nestrilabs/nesbox/jail:latest` sha256:df813051…, rebuilt on
that base from nesbox dev 6d37597, which includes the vhost-user
forwarding

Measured on box2 (RTX 3060, 595.104.02) with virtio-nvgpu's vkrt probe:
with the layer on, a plain device is created and none of the seven
extensions are offered. With it off, all seven are offered and fail with
-3.
This commit is contained in:
Wanjohi
2026-10-04 02:30:10 +00:00
committed by GitHub
3 changed files with 52 additions and 5 deletions
+39 -2
View File
@@ -168,6 +168,40 @@ RUN --mount=type=cache,target=/root/.cargo/registry \
find /artifacts/nestri -type f -printf '/%P\n' > /artifacts/nestri/.manifest
# ───────────────────────────────────────────────────────────
# virtio-nvgpu's Vulkan layer — for guests on an NVIDIA host
#
# On an NVIDIA host the guest runs NVIDIA's own Vulkan driver over a
# forwarded device, and that driver offers ray tracing, optical flow and a
# few others it then cannot create a device with: vkCreateDevice fails, and
# an engine that enables what it is offered does not start. The layer hides
# them. It acts only on an NVIDIA device in a guest with the virtio-nvgpu
# driver, so on an AMD or Intel box it is loaded and does nothing.
#
# Built from virtio-nvgpu's own tree at NVGPU_REF, the same ref the kernel
# build takes the guest driver from (build/Makefile), because the layer's job
# is to say what that driver and its backend can do. The commit is recorded
# in /usr/share/nestri/virtio-nvgpu.rev.
# ───────────────────────────────────────────────────────────
FROM builder AS nvgpu-layer-build
ARG NVGPU_GIT=https://github.com/nestrilabs/virtio-nvgpu.git
ARG NVGPU_REF=dev
RUN --mount=type=cache,target=/root/.cargo/registry \
git init -q /build/virtio-nvgpu && cd /build/virtio-nvgpu && \
git fetch -q --depth=1 "${NVGPU_GIT}" "${NVGPU_REF}" && git checkout -q FETCH_HEAD && \
cargo build --release -p nvgpu-vklayer && \
install -Dm755 target/release/libVkLayer_nvgpu.so \
/artifacts/nvgpu/usr/lib/libVkLayer_nvgpu.so && \
mkdir -p /artifacts/nvgpu/usr/share/vulkan/implicit_layer.d /artifacts/nvgpu/usr/share/nestri && \
sed 's|"./libVkLayer_nvgpu.so"|"libVkLayer_nvgpu.so"|' vklayer/VkLayer_nvgpu.json \
> /artifacts/nvgpu/usr/share/vulkan/implicit_layer.d/VkLayer_nvgpu.json && \
grep -q '"library_path": "libVkLayer_nvgpu.so"' \
/artifacts/nvgpu/usr/share/vulkan/implicit_layer.d/VkLayer_nvgpu.json && \
git rev-parse HEAD > /artifacts/nvgpu/usr/share/nestri/virtio-nvgpu.rev && \
cd / && rm -rf /build/virtio-nvgpu && \
find /artifacts/nvgpu -type f -printf '/%P\n' > /artifacts/nvgpu/.manifest
# ───────────────────────────────────────────────────────────
# umu-launcher — built here, not installed from the distribution
#
@@ -394,10 +428,12 @@ LABEL org.opencontainers.image.source="https://github.com/nestrilabs/nestri"
# purpose, not an oversight.
COPY --from=mesa-build /artifacts/mesa/.manifest /tmp/mesa.manifest
COPY --from=nestri-build /artifacts/nestri/.manifest /tmp/nestri.manifest
RUN cat /tmp/mesa.manifest /tmp/nestri.manifest > /tmp/.strip-manifest && \
rm -f /tmp/mesa.manifest /tmp/nestri.manifest
COPY --from=nvgpu-layer-build /artifacts/nvgpu/.manifest /tmp/nvgpu.manifest
RUN cat /tmp/mesa.manifest /tmp/nestri.manifest /tmp/nvgpu.manifest > /tmp/.strip-manifest && \
rm -f /tmp/mesa.manifest /tmp/nestri.manifest /tmp/nvgpu.manifest
COPY --from=mesa-build /artifacts/mesa /
COPY --from=nestri-build /artifacts/nestri /
COPY --from=nvgpu-layer-build /artifacts/nvgpu /
COPY --from=umu-build /artifacts/umu /
# **No Proton here.** It is attached to a box at run time, mounted at
@@ -520,6 +556,7 @@ RUN failed=0; \
: > /tmp/missing-libs; \
for f in /usr/bin/nesinit /usr/bin/nescope /usr/bin/neshub /usr/bin/neswire \
/usr/bin/nesgamepad /usr/lib/libnescapture_layer.so /usr/lib/libSDL2-2.0.so.0 \
/usr/lib/libVkLayer_nvgpu.so \
/usr/bin/dbus-daemon /usr/bin/pipewire \
/usr/bin/wireplumber /usr/bin/ip \
/usr/lib/libgallium-*.so /usr/lib/libEGL_mesa.so.0 \
+7 -3
View File
@@ -76,9 +76,11 @@ INFINITY_REV ?= e6c85d841f25e21393a7ea47fdef286318915d8c
INFINITY_SERIES := patches/cachyos/tuned-eevdf/$(KERNEL_REF:cachyos-%=%)
INFINITY_WORK := $(OUTPUT_DIR)/infinity-sched
# The guest half of NVIDIA forwarding, built into the kernel. A branch, so each
# kernel build takes the driver as it is now; set NVGPU_REF to a tag or commit
# to pin it. The commit built is recorded as $(KERNEL_OUTPUT).nvgpu-rev.
# The guest half of NVIDIA forwarding: the driver, built into the kernel, and
# its Vulkan layer, built into the rootfs. A branch, so each build takes them
# as they are now; set NVGPU_REF to a tag or commit to pin it. The commit built
# is recorded as $(KERNEL_OUTPUT).nvgpu-rev and, in the image, as
# /usr/share/nestri/virtio-nvgpu.rev. Build both from the same ref.
NVGPU_GIT ?= https://github.com/nestrilabs/virtio-nvgpu.git
NVGPU_REF ?= dev
NVGPU_WORK := $(OUTPUT_DIR)/virtio-nvgpu
@@ -94,11 +96,13 @@ endif
build:
DOCKER_BUILDKIT=1 $(CONTAINER_RT) build $(if $(FORCE_REBUILD),--no-cache,) \
--build-arg PROTON_IMAGE=$(PROTON_REF) \
--build-arg NVGPU_GIT=$(NVGPU_GIT) --build-arg NVGPU_REF=$(NVGPU_REF) \
-f Containerfile -t $(IMAGE_NAME):latest --target runtime_prod $(CONTEXT)
build-debug:
DOCKER_BUILDKIT=1 $(CONTAINER_RT) build $(if $(FORCE_REBUILD),--no-cache,) \
--build-arg PROTON_IMAGE=$(PROTON_REF) \
--build-arg NVGPU_GIT=$(NVGPU_GIT) --build-arg NVGPU_REF=$(NVGPU_REF) \
-f Containerfile -t $(IMAGE_NAME):debug --target runtime_debug $(CONTEXT)
# Hours, and only when PROTON_TAG moves. Two steps: the build runs on the host,
+6
View File
@@ -166,6 +166,12 @@ modules. The driver's parameters therefore go on the command line as
warns that it is doing so.
- An AMD or Intel host is unaffected. The driver binds only to the forwarding
device, and nothing offers that device to those guests.
- **The rootfs carries virtio-nvgpu's Vulkan layer**, built from the same
`NVGPU_REF`. NVIDIA's driver in a guest offers ray tracing and a few other
extensions it cannot then create a device with; the layer hides them. It
acts only on an NVIDIA device in a guest with the forwarding driver, so AMD
and Intel boxes are unaffected. Build the kernel and the image from the same
ref, or the layer describes a different driver than the one booted.
- The NVIDIA libraries are **not** part of the image. They must be the same
build as the host's kernel module, so the host shares its own copy with the
guest at run time.