Status: Draft (2026-10-05), reflecting actual measurements. The values in the "Actual Measurement Example" section were directly verified using read-only commands on a single GPU server, and identification information such as device IDs has been replaced with placeholders. The explanation of the principles is based on kernel and KubeVirt documentation, which was not re-opened and cross-checked at the time of writing this post (unverified). Unverified assumptions, such as the relationship between memory pinning and crash loops, are marked as such. Writing rule: Actual measurement data is the default, and anything that could not be measured is marked as "Unmeasured".
Table of Contents
- 1. Why Use Passthrough
- 2. Principles: Four Components
- 3. Actual Measurement Example: A Server with One 6GB-class GPU Passed Through
- 4. Why nvidia-smi Fails on the Same Device
- 5. How KubeVirt Connects Devices to VMs
- 6. Common Pitfalls: Memory Pinning and Host OOM
- 7. Reverting to a Container GPU (Example Procedure)
- 8. Likely Interview Questions
- 9. Common Misconceptions
- External Reference: Performance Overhead of Passthrough
- Unverified Items
- References
- Verified Versions and Dates
1. Why Use Passthrough
There are broadly four ways to provide a GPU to a workload.
| Method | Unit | Isolation | Relationship to This Post |
|---|---|---|---|
| Container (device plugin) | Single GPU or time-sliced | Process level, shares host kernel and drivers | Part 3 |
| MIG | Hardware-partitioned GPU (supported models only) | Hardware level | Part 3 |
| vGPU | Virtual GPU created by driver | Managed by hypervisor, requires license | Out of scope |
| Passthrough | Entire physical GPU | VM exclusively owns the device | This post |
The scenarios requiring passthrough narrow down to three: when the guest needs a different kernel or driver version than the host, when the guest OS is not Linux, and when isolation stronger than what containers can offer is required. Instead, that GPU cannot be used simultaneously by the host and containers. Later in this post, we will actually see the same device nvidia-smi failing on one side.
2. Principles: Four Components
Passthrough is the process of transferring device ownership from a general host kernel driver to a user-space process (QEMU). Four components work together to achieve this.
2.1 Driver Binding
PCI devices have a vendor ID and a device ID (for example, the GPU has 10de:XXXX), and the kernel attaches a single driver matching that ID. Normally, the nvidia driver is attached, and then nvidia-smi runs on the host. In pass-through, vfio-pci is attached instead. vfio-pci is not a driver that uses the device, but an intermediary driver that hands the device over to user space. A device with this driver attached is not used by the host kernel.
There are two ways to attach it. One is to switch it at runtime using driver_override and bind/unbind, and the other is to have vfio-pci claim the corresponding ID first at boot time. The example server uses the latter method (Section 3).
2.2 IOMMU: A Device That Restricts Device Memory Access
The GPU reads from and writes to system memory directly via DMA. When the device is given to the guest, the memory addresses used by the guest differ from the actual addresses of the host, so the device could touch unintended host memory. IOMMU translates the DMA addresses generated by the device and restricts the allowed rangeto prevent this issue. This is done by opening only the guest memory region to the device.
The minimum unit of isolation is the IOMMU group. Since devices belonging to the same group cannot isolate each other's DMA, all devices in the group must be handed over to the same owner . Therefore, the GPU's audio function (the 01:00.1in the example) is also bound together to vfio-pci .
2.3 VFIO Interface
VFIO is a kernel feature that safely exposes devices to user space. A user-space process opens /dev/vfio/vfio(container) and /dev/vfio/<그룹 번호> to do the following: map the device's BAR (memory region) into its own address space, register guest memory with the IOMMU, and receive the device's interrupts via event file descriptors to inject them into the guest. QEMU performs these tasks.
Since guest memory is the target of DMA, it must be pinned so that the host cannot reclaim or swap it out. A VM with 12GiB of memory means that 12GiB of host memory is pinned upon startup (based on documentation, not directly measured).
2.4 Reset
When a VM terminates, the device must be returned to a clean state for the safety of the next user. The best method is FLR (Function Level Reset) supported by the device, and if not supported, a bus reset is used. GPUs that do not reset properly cause an issue where the host must be rebooted after using the VM once.
3. Measurement Example: A server passing through a 6GB-class GPU
These values were all checked using read-only commands and are example valuesthat vary by equipment. The host kernel is 7.0.0-34-generic. The device IDs are masked as XXXX(GPU) and YYYY(audio).
| Item | Value | Meaning |
|---|---|---|
lspci -nnk -d 10de: |
01:00.0 GPU(10de:XXXX) and 01:00.1 audio(10de:YYYY) both Kernel driver in use: vfio-pci |
Both features vfio-pci is owned by |
| IOMMU group | Group 16, members are just 01:00.0 and 01:00.1 only |
The group is clean so this GPU can be passed through separately |
| Kernel command line | iommu related arguments none, but there are 20 IOMMU groups |
In this kernel, IOMMU is enabled by default |
| Loaded modules | vfio_pci, vfio_pci_core, vfio_iommu_type1, vfio, kvm_intel |
VFIO stack is up |
/dev/vfio/ |
16(group device) and vfio(container) |
Ready to be handed over to user space |
reset_method |
flr bus |
Supports FLR, falls back to bus reset if not supported |
| BAR regions | Region 0 is 16M, Region 1 is 8G (prefetchable), Region 3 is 32M | The GPU's memory window will be mapped to the guest |
| PCIe link | capability 16GT/s x16, current 16GT/s x8 (downgraded) |
link width has dropped by half |
The cause for the link width dropping to x8 could not be verified. Since it could be due to board wiring or power states, no definitive conclusion is made.
3.1 Configuration files
In this example, the setup that creates the passthrough consists of two files.
# /etc/modprobe.d/vfio-gpu.conf
options vfio-pci ids=10de:XXXX,10de:YYYY
softdep nvidia pre: vfio-pci
softdep nouveau pre: vfio-pci
softdep snd_hda_intel pre: vfio-pci
# /etc/modules-load.d/vfio-pci.conf
vfio-pci
options vfio-pci ids=…makesvfio-pciclaim these two IDs as its own.softdep … pre: vfio-pciloadsnvidia,nouveau,snd_hda_intelbeforevfio-pciis loaded, preventing those drivers from taking the device first.modules-load.dloads thevfio-pcimodule itself at boot time.
The example server's boot image (initramfs) did not contain a single vfio entry. Therefore, this configuration works purely with files without requiring an image regeneration, and it can be reverted for the same reason by removing the files (though it has not yet been tested whether it actually reverts after a reboot).
4. Why nvidia-smi fails on the same device
Running nvidia-smi on this host outputs a message saying "Failed to initialize NVML: Driver/library version mismatch" or that it cannot communicate with the NVIDIA driver. This does not mean the driver is not installed. The host has nvidia-driver-595-open(595.91.07) installed, along with the module package for the current kernel.
The kernel logs show the cause clearly.
NVRM: GPU 0000:01:00.0 is already bound to vfio-pci.
nvidia Every time the module attempts to claim the device, a message repeatedly appears stating that it is already owned by vfio-pci . Since there is only one driver attached to the device, the host's nvidia-smi has nothing to communicate with while ownership is held by VFIO. This is not a malfunction, but normal operation as configured.
The Kubernetes side status tells the same story.
| Location | Value |
|---|---|
Node nvidia.com/gpu |
0 (No GPU to give to containers) |
Node nvidia.com/<모델별 자원 이름> |
1 (Device available to give to VM) |
Node label nvidia.com/gpu.deploy.operands |
false (GPU Operator components disabled) |
GPU Operator driver.enabled |
false (Configuration using the driver installed on the host) |
5. How KubeVirt connects a device to a VM
- In the KubeVirt configuration,
permittedHostDevicesandpciVendorSelector: 10DE:XXXXare registered underresourceName: nvidia.com/<모델별 자원 이름>, and the feature gateHostDevicesis enabled. - The node's
virt-handlerfinds the device with that ID and advertises it as a Kubernetes extended resource. The result is thenvidia.com/<모델별 자원 이름>: 1in the table above. - Write the
devices.gpusin the VM definition todeviceName: nvidia.com/<모델별 자원 이름>. The scheduler places the VM only on nodes that have this resource. - When the VM starts, QEMU opens the corresponding IOMMU group via VFIO and attaches the device to the guest.
The important point here is that a separate name is used for the resource name, nvidia.com/gpu not. GPUs for containers and GPUs for VMs are different resources, and a single GPU is advertised as only one of the two.
6. Common Pitfall: Memory Pinning and Host OOM
A VM passed through a GPU pins its guest memory to the host, as described in Section 2.3. Since pinned memory cannot be absorbed by swap, the host needs an amount of memory equal to the guest size all at once the moment the VM starts. If other workloads and node daemons are running together on the same host, the total easily exceeds the physical memory.
If the host as a whole runs out of memory, the kernel's OOM killer activates. At this time, the kernel log records a host-wide out-of-memory error (Memory cgroup out of memory), not a container limit exceeded error (Out of memory: Killed process). The victim may not only be the VM process (qemu-kvm) but also a system daemon of the node. If the VM dies and restarts, it becomes a crash loop repeating the same thing, while the daemons also continue to die along with it.
The host OOM logs observed in the author's experimental environment matched this flow. However, since the actual pinned memory size was not measured when the VM started, this interpretation is an estimate(unmeasured). There are two lessons: it is safer not to place multiple GPU VMs on a server where host memory is not ample, and before bringing up a VM, you should first add up the sum of the guest memory and the memory already used by the host.
7. Reverting to Container GPUs (Example Procedure)
The simplest way to unbind passthrough and revert the device for containers is to cordon and drain the node, clear the configuration files, and reboot. However, if other services are running on that node, a reboot becomes a major operation that takes everything down. Below is an example of transferring only the device without a reboot . The basis for determining that this was possible was first verified entirely in read-only mode.
| Pre-check | Result |
|---|---|
/dev/vfio/<그룹 번호> Processes holding the |
None |
| VMs using the GPU | None |
| Device status | Idle, reset method flr bus supported |
nvidia Module |
595.91.07, for current kernel, signed |
First, we backed up two configuration files, removed them, and executed the following two steps.
# 1) 이 장치는 nvidia 드라이버로만 붙도록 지정한다.
echo nvidia | sudo tee /sys/bus/pci/devices/0000:01:00.0/driver_override
# 2) vfio-pci 에서 푼다. 풀리는 즉시 nvidia 가 장치를 가져간다.
echo 0000:01:00.0 | sudo tee /sys/bus/pci/drivers/vfio-pci/unbind
Result. unbind Immediately afterwards, the device driver changed to nvidia and nvidia-smi operated.
| Item | Value |
|---|---|
| GPU | 6GB class laptop GPU, 6,144 MiB |
| Driver | 595.91.07 |
lspci |
01:00.0 's Kernel driver in use: nvidia |
| Host | Uptime maintained, no load change, no impact on other services |
Points to note together.
- The audio function (
01:00.1) remains as it is invfio-pci. We assume there is no impact on GPU usage for containers, but this was not verified. - Two lines of
NVRM: … NV_ERR_INVALID_DATA(PlatformRequest) warnings were printed to the kernel log, andpower.drawwas read as approximately 752W. This is an impossible number for the power value of this GPU, suggesting an issue with reading sensor or platform information, the cause of which was not identified. Do not trust the power value immediately after switching. - We did not test whether
nvidiaautomatically attaches even after a reboot. Since we removed the configuration file, we expect it to attach, but this is still an assumption. - The Kubernetes side remains unchanged. The node's
nvidia.com/gpuis still0, and KubeVirt'snvidia.com/<모델별 자원 이름>also remains1(this appears to be a stale value untilvirt-handlerreads it again). For containers to use the GPU, you must change the node labelnvidia.com/gpu.deploy.operandstotrueto enable the GPU Operator components.
To revert, change driver_override to vfio-pci and release nvidia . After this switch, GPU VMs cannot use this GPU. If a single GPU must be shared alternately between containers and VMs, this switch must be performed every time,so in environments with a small number of GPUs, the use case must be fixed to one.
8. Potential Interview Questions
- How do passthrough, vGPU, and MIG differ? They are distinguished by their isolation unit (entire device, software partitioning, hardware partitioning), licensing, and supported hardware.
- Why are IOMMU groups important? Devices in the same group cannot isolate DMA from each other and must be passed through together. The example server's group was clean as it contained only the GPU and audio.
- Why is host memory pinned when a VM starts? Because the device performs DMA on guest memory, ensuring that region cannot be moved or swapped.
- If
nvidia-smidoes not work on the host even though the driver is installed? First check the driver attached to the device (iflspci -nnk).vfio-pciis attached, it is operating according to the configuration). - Can this GPU be shared simultaneously by containers and VMs? Not with the passthrough method. Time-sharing, MIG, or vGPU must be considered.
9. Common Misconceptions
- "The driver is broken": The ownership may simply belong to a different driver.
- "Passthrough results in no performance loss": The device's own performance remains almost identical, but operational overheads such as link width, resets, and memory pinning follow. Performance loss was not measured on the example server (unmeasured; since it is a service-running server, we did not switch the GPU back to passthrough to test). External measurements are summarized in "External Evidence" below.
- "IOMMU must be enabled separately": It was enabled by default without arguments on the example kernel. Since it varies depending on hardware and kernel configurations, first verify whether the groups are visible.
External Evidence: Performance Overhead of Passthrough
Since we could not directly measure the performance of the passthrough VM, we checked external materials that measured the same question. The accessible scope includes abstracts, vendor articles, and some original texts.
| Source | Verified Content and Conditions | Reliability and Limitations |
|---|---|---|
| Walters et al., "GPU Passthrough Performance: A Comparison of KVM, Xen, VMWare ESXi, and LXC for CUDA and OpenCL Applications", IEEE CLOUD 2014 | Abstract: KVM passthrough achieves 98-100% of bare metal, Xen and VMware achieve 96-99% (two generations of GPUs, two processor microarchitectures) | Peer-reviewed paper or main text could not be accessed, so IOMMU, huge pages, NUMA, and link width conditions are unknown. 2014 GPU |
| Steininger et al., "GPU Passthrough Across Virtualization Platforms for LLM Inference", Computers (MDPI) Vol. 15, 2026 | Abstract: Passing through a single workstation-class GPU across Proxmox VM/LXC, KVM, OpenStack, and Podman results in vLLM small model throughput and inter-token latency within 2% of bare metal across all platforms (statistically equivalent) | Authors state this is not a general claim. Tested in a region that does not saturate the device. Only the abstract was verified due to denied access to the main text |
| OpenNebula Vendor Blog (2026-03-18) | GB200 NVL4 passthrough cuBLAS GEMM 71,891 vs 71,738 GFLOP/s (0.21% difference), vLLM throughput within ±4% | Vendor article; NUMA pinning, huge pages, and IOMMU configurations are not specified |
| VMware Performance Study (2023-10) | GPT-J 6B achieves 97.9-99.8% of bare metal with vGPU (full profile H100) (MLPerf Inference v3.1) | vGPU rather than passthrough. Results not verified by MLCommons |
| Samani, Amini Salehi, arXiv 2112.09780, 2021 | Comparison of bare metal, VM (passthrough), and container. There is no consistent single overhead; end-to-end inference time is dominated by CPU preprocessing and storage I/O, making the GPU portion's impact minor. If there are no idle cores on the host, VM overhead increases, which CPU pinning mitigates | Tesla K40, 2-socket 80-core, HDD. Aging GPU |
| NVIDIA CUDA Programming Guide 3.4.2.5, NCCL GPU troubleshooting | States to disable IOMMU on bare metal and enable IOMMU and use VFIO for VM passthrough. ACS bypasses P2P to the CPU root complex, significantly reducing bandwidth; VMs cannot disable ACS, and maximizing performance inside a VM requires network adapter ATS | Official documentation. P2P is not an issue on single-GPU servers |
- Verdict: Moderately supported. "Device performance is almost identical" is consistent with the 0-5% range reported by external measurements. However, since the verified figures are from abstracts, vendor articles, and non-saturating workloads, we could not obtain figures under the exact same conditions as our GPU. Primary data measured with link width, huge pages, and IOMMU settings as variables was not found.
- Considering that the KubeVirt documentation guides to enable kernel arguments (
intel_iommu=on,amd_iommu=on) while distribution defaults differ, "first verify whether groups are visible" from this article is a safe statement.
Unverified Items
- The actual memory size pinned when the GPU VM starts (the interpretation in Section 6 is an assumption consistent with logs).
- The cause of the PCIe link dropping to x8.
- GPU performance inside the passthrough VM (throughput compared to containers was unmeasured; since production services run on the same server, tests to hand the GPU back to the VM were omitted, and external measurements were used instead).
- Whether
nvidiaautomatically attaches after a reboot (we switched without a reboot, and reboots were not tested). - This article's explanation of principles did not re-open and cross-reference the kernel VFIO documentation and KubeVirt host device documentation this time.
References
- Kernel VFIO Documentation: https://www.kernel.org/doc/html/latest/driver-api/vfio.html
- KubeVirt Host Devices User Guide: https://kubevirt.io/user-guide/compute/host-devices/
Verified Version and Date
As of 2026-10-05. Host kernel 7.0.0-34-generic, NVIDIA driver package 595.91.07 (installed on host), KubeVirt HostDevices feature gate enabled.