Setting up a Virtual Workstation in OpenShift with VFIO Passthrough¶
Feb 27, 2023
25 min read
This guide explains how to configure OpenShift as a workstation with GPU PCI passthrough using Container Native Virtualization (CNV) on a single OpenShift node (SNO). This setup delivers near-native performance for GPU-intensive applications while leveraging Kubernetes orchestration capabilities.
Key Benefits:
Run containerized workloads and virtual machines on the same hardware
Use a single GPU for both Kubernetes pods and VMs by switching driver binding
Achieve near-native performance for gaming and professional applications in VMs
Maintain Kubernetes/OpenShift flexibility for other workloads
In testing, this configuration successfully ran Microsoft Flight Simulator in a Windows VM with performance comparable to a bare metal Windows installation.
Hardware Used:
Component |
Specification |
---|---|
CPU |
AMD Ryzen 9 3950X (16-Core, 32-Threads) |
Memory |
64GB DDR4 3200MHz |
GPU |
Nvidia RTX 3080 FE 10GB |
Storage |
2x 2TB NVMe Disks (VM storage)
1x 500GB SSD Disk (OpenShift root system)
|
Network |
10Gbase-CX4 Mellanox Ethernet |
Similar configurations with Intel CPUs should work with minor adjustments noted throughout this guide.
Installing OpenShift SNO¶
Before installation, be sure to back up any existing partition data.
Backup Existing System Partitions¶
The OpenShift assisted installer formats the first 512 bytes of any disk with a bootable partition. Back up and remove any existing partition tables you want to preserve.
OpenShift Installation¶
Note
You can use the OpenShift web UI installer in Red Hat Hybrid Cloud Console. This guides you through installation with the Assisted Installer service:
https://console.redhat.com/openshift/assisted-installer/clusters
This also provides an automated way to install multiple Operators from Day 0.
- Relevant Operators for this setup:
Logical Volume Manager Storage
NMState
Node Feature Discovery
NVIDIA GPU
OpenShift Virtualization
After backing up existing file systems and removing bootable partitions, proceed with the OpenShift Single Node installation.
CoreOS (the underlying operating system) requires an entire disk for installation:
500GB SSD for the OpenShift operating system
Two 2TB NVMe disks for persistent volumes as LVM Physical volumes in the same Volume Group
This setup enables flexible VM storage management while keeping the system installation separate
1#!/bin/bash
2
3OCP_VERSION=latest-4.10
4
5curl -k https://mirror.openshift.com/pub/openshift-v4/clients/ocp/latest/openshift-client-linux.tar.gz > oc.tar.gz
6tar zxf oc.tar.gz
7chmod +x oc && mv oc ~/.local/bin/
8
9curl -k https://mirror.openshift.com/pub/openshift-v4/clients/ocp/$OCP_VERSION/openshift-install-linux.tar.gz > openshift-install-linux.tar.gz
10tar zxvf openshift-install-linux.tar.gz
11chmod +x openshift-install && mv openshift-install ~/.local/bin/
12
13curl $(openshift-install coreos print-stream-json | grep location | grep x86_64 | grep iso | cut -d\" -f4) > rhcos-live.x86_64.iso
1# This file contains the configuration for an OpenShift cluster installation.
2
3apiVersion: v1
4
5# The base domain for the cluster.
6baseDomain: epheo.eu
7
8# Configuration for the compute nodes.
9compute:
10- name: worker
11 replicas: 0
12
13# Configuration for the control plane nodes.
14controlPlane:
15 name: master
16 replicas: 1
17
18# Metadata for the cluster.
19metadata:
20 name: da2
21
22# Networking configuration for the cluster.
23networking:
24 networkType: OVNKubernetes
25 clusterNetwork:
26 - cidr: 10.128.0.0/14
27 hostPrefix: 23
28 serviceNetwork:
29 - 172.30.0.0/16
30
31# Platform configuration for the cluster.
32platform:
33 none: {}
34
35# Configuration for bootstrapping the cluster.
36bootstrapInPlace:
37 installationDisk: /dev/sda
38
39# Pull secret for accessing the OpenShift registry.
40pullSecret: '{"auths":{"cloud.openshift.com":{"auth":"XXXXXXXX"}}}'
41
42# SSH key for accessing the cluster nodes.
43sshKey: |
44 ssh-rsa AAAAB3XXXXXXXXXXXXXXXXXXXXXXXXX
mkdir ocp && cp install-config.yaml ocp
openshift-install --dir=ocp create single-node-ignition-config
alias coreos-installer='podman run --privileged --rm \
-v /dev:/dev -v /run/udev:/run/udev -v $PWD:/data \
-w /data quay.io/coreos/coreos-installer:release'
cp ocp/bootstrap-in-place-for-live-iso.ign iso.ign
coreos-installer iso ignition embed -fi iso.ign rhcos-live.x86_64.iso
dd if=discovery_image_sno.iso of=/dev/usbkey status=progress
After copying the ISO to a USB drive, boot your workstation from it to install OpenShift.
Installing CNV Operator¶
Enable Intel VT or AMD-V hardware virtualization extensions in your BIOS/UEFI settings.
1# This YAML file contains Kubernetes resources for installing the KubeVirt Hyperconverged Operator (HCO) on the OpenShift Container Platform.
2# It creates a namespace named "openshift-cnv", an operator group named "kubevirt-hyperconverged-group" in the "openshift-cnv" namespace, and a subscription named "hco-operatorhub" in the "openshift-cnv" namespace.
3# The subscription specifies the source, source namespace, name, starting CSV, and channel for the KubeVirt Hyperconverged Operator.
4
5apiVersion: v1
6kind: Namespace
7metadata:
8 name: openshift-cnv
9---
10apiVersion: operators.coreos.com/v1
11kind: OperatorGroup
12metadata:
13 name: kubevirt-hyperconverged-group
14 namespace: openshift-cnv
15spec:
16 targetNamespaces:
17 - openshift-cnv
18---
19apiVersion: operators.coreos.com/v1alpha1
20kind: Subscription
21metadata:
22 name: hco-operatorhub
23 namespace: openshift-cnv
24spec:
25 source: redhat-operators
26 sourceNamespace: openshift-marketplace
27 name: kubevirt-hyperconverged
28 startingCSV: kubevirt-hyperconverged-operator.v4.10.0
29 channel: "stable"
oc apply -f cnv-resources.yaml
subscription-manager repos --enable cnv-4.10-for-rhel-8-x86_64-rpms
dnf install kubevirt-virtctl
Configuring OpenShift for GPU Passthrough¶
Since we’re working with a single GPU, additional configuration is required.
We’ll use MachineConfig to configure our node. In a single-node OpenShift setup, all MachineConfig changes apply to the master machineset. In multi-node clusters, these would apply to workers instead.
See also
https://github.com/openshift/machine-config-operator/blob/master/docs/SingleNodeOpenShift.md
Setting Kernel Boot Arguments¶
To enable GPU passthrough, we need to pass several kernel arguments at boot time via the MachineConfigOperator:
amd_iommu=on: Enables IOMMU support for AMD platforms (use intel_iommu=on for Intel CPUs)
vga=off: Disables VGA console output during boot
rdblaclist=nouveau: Blacklists the Nouveau open-source NVIDIA driver
video=efifb:off: Disables EFI framebuffer console output
See also
https://www.reddit.com/r/VFIO/comments/cktnhv/bar_0_cant_reserve/ https://www.reddit.com/r/VFIO/comments/mx5td8/bar_3_cant_reserve_mem_0xc00000000xc1ffffff_64bit/ https://docs.kernel.org/fb/fbcon.html
1variant: openshift
2version: 4.10.0
3metadata:
4 name: 100-vfio
5 labels:
6 machineconfiguration.openshift.io/role: master
7openshift:
8 kernel_arguments:
9 - amd_iommu=on
10 - vga=off
11 - rdblaclist=nouveau
12 - 'video=efifb:off'
1cd articles/openshift-workstation/machineconfig/build
2butane -d . vfio-prepare.bu -o ../vfio-prepare.yaml
3oc patch MachineConfig 100-vfio --type=merge -p ../vfio-prepare.yaml
Note
Intel CPU users: use intel_iommu=on instead of amd_iommu=on.
Installing the NVIDIA GPU Operator¶
The NVIDIA GPU Operator simplifies GPU management in Kubernetes environments.
Step 1: Install the Operator¶
Via OpenShift web console: 1. Go to Operators → OperatorHub 2. Search for “NVIDIA GPU Operator” 3. Select the operator and click Install 4. Keep default settings and click Install
Or via CLI:
oc create -f https://raw.githubusercontent.com/NVIDIA/gpu-operator/master/deployments/git/operator-namespace.yaml
oc create -f https://raw.githubusercontent.com/NVIDIA/gpu-operator/master/deployments/git/operator-source.yaml
Step 2: Configure the ClusterPolicy¶
Set sandboxWorkloads.enabled
to true
to enable the components needed for GPU passthrough:
1kind: ClusterPolicy
2metadata:
3 name: gpu-cluster-policy
4spec:
5 sandboxWorkloads:
6 defaultWorkload: container
7 enabled: true
oc patch ClusterPolicy gpu-cluster-policy --type=merge -p sandboxWorkloadsEnabled.yaml
The NVIDIA GPU Operator doesn’t officially support consumer-grade GPUs and won’t automatically bind the GPU audio device to the vfio-pci driver. We’ll handle this manually with the following machine config:
1variant: openshift
2version: 4.10.0
3metadata:
4 name: 100-vfio
5 labels:
6 machineconfiguration.openshift.io/role: master
7storage:
8 files:
9 - path: /usr/local/bin/vfio-prepare
10 mode: 0755
11 overwrite: true
12 contents:
13 local: ./vfio-prepare.sh
14 - path: /etc/modules-load.d/vfio-pci.conf
15 mode: 0644
16 overwrite: true
17 contents:
18 inline: vfio-pci
19systemd:
20 units:
21 - name: vfioprepare.service
22 enabled: true
23 contents: |
24 [Unit]
25 Description=Prepare vfio devices
26 After=ignition-firstboot-complete.service
27 Before=kubelet.service crio.service
28
29 [Service]
30 Type=oneshot
31 ExecStart=/usr/local/bin/vfio-prepare
32
33 [Install]
34 WantedBy=kubelet.service
1#!/bin/bash
2
3vfio_attach () {
4 if [ -f "${path}/driver/unbind" ]; then
5 echo $address > ${path}/driver/unbind
6 fi
7 echo vfio-pci > ${path}/driver_override
8 echo $address > /sys/bus/pci/drivers/vfio-pci/bind || \
9 echo $name > /sys/bus/pci/drivers/vfio-pci/new_id ||true
10}
11
12# 0a:00.1 Audio device [0403]: NVIDIA Corporation GA102 High Definition Audio Controller [10de:1aef] (rev a1)
13address=0000:0a:00.1
14path=/sys/bus/pci/devices/0000\:0a\:00.1
15name="10de 1467"
16vfio_attach
1cd articles/openshift-workstation/machineconfig/build
2butane -d . vfio-prepare.bu -o ../vfio-prepare.yaml
3oc patch MachineConfig 100-vfio --type=merge -p ../vfio-prepare.yaml
Dynamically Switching GPU Drivers¶
A key advantage of this setup is using a single GPU for both container workloads and VMs without rebooting.
Use Case Scenario¶
Single NVIDIA GPU shared between container workloads and VMs
Container workloads require the NVIDIA kernel driver
VMs with GPU passthrough require the VFIO-PCI driver
Switching between modes without rebooting
Driver Switching Using Node Labels¶
The NVIDIA GPU Operator with sandbox workloads enabled lets you switch driver bindings using node labels:
For container workloads (NVIDIA driver):
# Replace 'da2' with your node name
oc label node da2 --overwrite nvidia.com/gpu.workload.config=container
For VM passthrough (VFIO-PCI driver):
# Replace 'da2' with your node name
oc label node da2 --overwrite nvidia.com/gpu.workload.config=vm-passthrough
Notes on Driver Switching¶
Driver switching takes a few minutes
Verify current driver with
lspci -nnk | grep -A3 NVIDIA
Stop all GPU workloads before switching drivers
No reboot is usually required
Can be occasionally unreliable and may require a reboot
Adding GPU as a Hardware Device¶
See also
https://github.com/kubevirt/kubevirt/blob/main/docs/devel/host-devices-and-device-plugins.md
First, identify the GPU’s Vendor and Product ID:
lspci -nnk |grep VGA
Then, identify the device name provided by gpu-feature-discovery:
oc get nodes da2 -ojson |jq .status.capacity |grep nvidia
Now, add the GPU to the permitted host devices:
1kind: HyperConverged
2metadata:
3 name: kubevirt-hyperconverged
4 namespace: openshift-cnv
5spec:
6 permittedHostDevices:
7 pciHostDevices:
8 - externalResourceProvider: true
9 pciDeviceSelector: 10DE:2206
10 resourceName: nvidia.com/GA102_GEFORCE_RTX_3080
oc patch hyperconverged kubevirt-hyperconverged -n openshift-cnv --type=merge -f hyperconverged.yaml
The pciDeviceSelector specifies the vendor:device ID, while resourceName specifies the resource name in Kubernetes/OpenShift.
Passthrough USB Controllers to VMs¶
For a complete desktop experience, you’ll want to pass through an entire USB controller to your VM for better performance and flexibility.
Identifying a Suitable USB Controller¶
List all USB controllers:
lspci -nnk | grep -i usb
Example output:
` 0b:00.3 USB controller [0c03]: Advanced Micro Devices, Inc. [AMD] Matisse USB 3.0 Host Controller [1022:149c] `
Note the PCI address (e.g., 0b:00.3) and device ID (1022:149c).
Check the IOMMU group:
find /sys/kernel/iommu_groups/ -iname "*0b:00.3*" # Shows which IOMMU group contains this device ls /sys/kernel/iommu_groups/27/devices/ # Lists all devices in the same IOMMU group
Important: For clean passthrough, the USB controller should ideally be alone in its IOMMU group. If other devices share the group, you’ll need to pass those through as well.
Adding the USB Controller as a Permitted Device¶
See also
https://access.redhat.com/solutions/6250271 https://kubevirt.io/user-guide/virtual_machines/host-devices/#listing-permitted-devices
Add the controller’s Vendor and Product IDs to permitted host devices:
1kind: HyperConverged
2metadata:
3 name: kubevirt-hyperconverged
4 namespace: openshift-cnv
5spec:
6 permittedHostDevices:
7 pciHostDevices:
8 - pciDeviceSelector: 1022:149C
9 resourceName: devices.kubevirt.io/USB3_Controller
10 - pciDeviceSelector: 8086:2723
11 resourceName: intel.com/WIFI_Controller
oc patch hyperconverged kubevirt-hyperconverged -n openshift-cnv --type=merge -f hyperconverged.yaml
Binding the USB Controller to VFIO-PCI Driver¶
1variant: openshift
2version: 4.10.0
3metadata:
4 name: 100-vfio
5 labels:
6 machineconfiguration.openshift.io/role: master
7storage:
8 files:
9 - path: /usr/local/bin/vfio-prepare
10 mode: 0755
11 overwrite: true
12 contents:
13 local: ./vfio-prepare.sh
14 - path: /etc/modules-load.d/vfio-pci.conf
15 mode: 0644
16 overwrite: true
17 contents:
18 inline: vfio-pci
19 - path: /etc/modprobe.d/vfio.conf
20 mode: 0644
21 overwrite: true
22 contents:
23 inline: |
24 options vfio-pci ids=8086:2723,1022:149c
25systemd:
26 units:
27 - name: vfioprepare.service
28 enabled: true
29 contents: |
30 [Unit]
31 Description=Prepare vfio devices
32 After=ignition-firstboot-complete.service
33 Before=kubelet.service crio.service
34
35 [Service]
36 Type=oneshot
37 ExecStart=/usr/local/bin/vfio-prepare
38
39 [Install]
40 WantedBy=kubelet.service
41openshift:
42 kernel_arguments:
43 - amd_iommu=on
44 - vga=off
45 - rdblaclist=nouveau
46 - 'video=efifb:off'
Create a script to unbind the USB controller from its current driver and bind it to vfio-pci:
1#!/bin/bash
2
3vfio_attach () {
4 if [ -f "${path}/driver/unbind" ]; then
5 echo $address > ${path}/driver/unbind
6 fi
7 echo vfio-pci > ${path}/driver_override
8 echo $address > /sys/bus/pci/drivers/vfio-pci/bind || \
9 echo $name > /sys/bus/pci/drivers/vfio-pci/new_id ||true
10}
11
12# 0a:00.1 Audio device [0403]: NVIDIA Corporation GA102 High Definition Audio Controller [10de:1aef] (rev a1)
13address=0000:0a:00.1
14path=/sys/bus/pci/devices/0000\:0a\:00.1
15name="10de 1467"
16vfio_attach
17
18# Bind "useless" device to vfio-pci to satisfy IOMMU group
19address=0000:07:00.0
20path=/sys/bus/pci/devices/0000\:07\:00.0
21name="1043 87c0"
22vfio_attach
23
24# Unbind USB switch and handle via vfio-pci kernel driver
25address=0000:07:00.1
26path=/sys/bus/pci/devices/0000\:07\:00.1
27name="1043 87c0"
28vfio_attach
29
30# Unbind USB switch and handle via vfio-pci kernel driver
31address=0000:07:00.3
32path=/sys/bus/pci/devices/0000\:07\:00.3
33name="1022 149c"
34vfio_attach
35
36# Unbind USB switch and handle via vfio-pci kernel driver
37address=0000:0c:00.3
38path=/sys/bus/pci/devices/0000\:0c\:00.3
39name="1022 148c"
40vfio_attach
1cd articles/openshift-workstation/machineconfig/build
2butane -d . vfio-prepare.bu -o ../vfio-prepare.yaml
3oc patch MachineConfig 100-vfio --type=merge -p ../vfio-prepare.yaml
Creating VMs with GPU Passthrough¶
This section explains how to create VMs that can use GPU passthrough, using existing LVM Logical Volumes with UEFI boot.
Creating Persistent Volumes from LVM Disks¶
First, make LVM volumes available to OpenShift via Persistent Volume Claims (PVCs). This assumes you have the Local Storage Operator installed.
Create a YAML file for each VM disk:
1---
2kind: PersistentVolumeClaim
3apiVersion: v1
4metadata:
5 name: fedora35
6spec:
7 accessModes:
8 - ReadWriteOnce
9 volumeMode: Block
10 resources:
11 requests:
12 storage: 100Gi
13 storageClassName: lvms-vg1
Apply the YAML:
oc apply -f fedora35.yaml
Verify the PV and PVC are bound:
oc get pv
oc get pvc -n <your-namespace>
Defining VMs with GPU Passthrough¶
Key configuration elements for desktop VMs with GPU passthrough:
GPU Passthrough: Pass the entire physical GPU to the VM
Disable Virtual VGA: Remove the emulated VGA device
USB Controller Passthrough: For connecting peripherals directly
UEFI Boot: For compatibility with modern OSes and GPU drivers
CPU/Memory Configuration: Based on workload requirements
1apiVersion: kubevirt.io/v1
2kind: VirtualMachine
3metadata:
4 name: fedora
5 namespace: epheo
6spec:
7 runStrategy: Halted
8 template:
9 metadata:
10 labels:
11 kubevirt.io/domain: fedora
12 spec:
13 architecture: amd64
14 domain:
15 cpu:
16 cores: 8
17 model: host-passthrough
18 sockets: 2
19 threads: 1
20 features:
21 acpi: {}
22 smm:
23 enabled: true
24 firmware:
25 bootloader:
26 efi:
27 secureBoot: false # For Nvidia Driver...
28 devices:
29 disks:
30 - bootOrder: 1
31 disk:
32 bus: virtio
33 name: pvdisk
34 - disk:
35 bus: virtio
36 name: cloudinitdisk
37 autoattachGraphicsDevice: false
38 gpus:
39 - deviceName: nvidia.com/GA102_GEFORCE_RTX_3080
40 name: gpuvideo
41 hostDevices:
42 - deviceName: devices.kubevirt.io/USB3_Controller
43 name: usbcontroller
44 - deviceName: devices.kubevirt.io/USB3_Controller
45 name: usbcontroller2
46 - deviceName: intel.com/WIFI_Controller
47 name: wificontroller
48 interfaces:
49 - masquerade: {}
50 name: default
51 - bridge: {}
52 model: virtio
53 name: nic-0
54 networkInterfaceMultiqueue: true
55 rng: {}
56 machine:
57 type: q35
58 resources:
59 requests:
60 memory: 16G
61 hostname: fedora
62 networks:
63 - name: default
64 pod: {}
65 - multus:
66 networkName: br1
67 name: nic-0
68 terminationGracePeriodSeconds: 0
69 volumes:
70 - persistentVolumeClaim:
71 claimName: 'fedora35'
72 name: pvdisk
73 - cloudInitNoCloud:
74 userData: |-
75 #cloud-config
76 password: fedora
77 chpasswd: { expire: False }
78 name: cloudinitdisk
1apiVersion: kubevirt.io/v1
2kind: VirtualMachine
3metadata:
4 annotations:
5 vm.kubevirt.io/os: windows10
6 vm.kubevirt.io/workload: desktop
7 name: windows
8spec:
9 runStrategy: Manual
10 template:
11 metadata:
12 labels:
13 kubevirt.io/domain: windows
14 spec:
15 architecture: amd64
16 domain:
17 clock:
18 timer:
19 hpet:
20 present: false
21 hyperv: {}
22 pit:
23 tickPolicy: delay
24 rtc:
25 tickPolicy: catchup
26 utc: {}
27 cpu:
28 cores: 8
29 dedicatedCpuPlacement: true
30 sockets: 2
31 threads: 1
32 devices:
33 autoattachGraphicsDevice: false
34 disks:
35 - cdrom:
36 bus: sata
37 name: windows-guest-tools
38 - bootOrder: 1
39 disk:
40 bus: virtio
41 name: pvdisk
42 - disk:
43 bus: virtio
44 name: pvdisk1
45 gpus:
46 - deviceName: nvidia.com/GA102_GEFORCE_RTX_3080
47 name: gpuvideo
48 hostDevices:
49 - deviceName: devices.kubevirt.io/USB3_Controller
50 name: usbcontroller
51 - deviceName: devices.kubevirt.io/USB3_Controller
52 name: usbcontroller2
53 - deviceName: intel.com/WIFI_Controller
54 name: wificontroller
55 interfaces:
56 - bridge: {}
57 model: virtio
58 name: nic-0
59 networkInterfaceMultiqueue: true
60 rng: {}
61 tpm: {}
62 features:
63 acpi: {}
64 apic: {}
65 hyperv:
66 frequencies: {}
67 ipi: {}
68 reenlightenment: {}
69 relaxed: {}
70 reset: {}
71 runtime: {}
72 spinlocks:
73 spinlocks: 8191
74 synic: {}
75 synictimer:
76 direct: {}
77 tlbflush: {}
78 vapic: {}
79 vpindex: {}
80 smm: {}
81 firmware:
82 bootloader:
83 efi:
84 secureBoot: true
85 machine:
86 type: q35
87 memory:
88 hugepages:
89 pageSize: 1Gi
90 resources:
91 requests:
92 memory: 32Gi
93 evictionStrategy: None
94 hostname: windows
95 networks:
96 - multus:
97 networkName: br1
98 name: nic-0
99 terminationGracePeriodSeconds: 3600
100 volumes:
101 - containerDisk:
102 image: registry.redhat.io/container-native-virtualization/virtio-win-rhel9@sha256:0c536c7aba76eb9c1e75a8f2dc2bbfa017e90314d55b242599ea41f42ba4434f
103 name: windows-guest-tools
104 - name: pvdisk
105 persistentVolumeClaim:
106 claimName: windows
107 - name: pvdisk1
108 persistentVolumeClaim:
109 claimName: windowsdata
Future Improvements¶
Some potential future improvements to this setup:
Using MicroShift instead of OpenShift to reduce Control Plane footprint
Running Linux Desktop in containers instead of VMs
Implementing more efficient resource allocation with CPU pinning and huge pages
Troubleshooting¶
IOMMU Group Issues¶
Problem: VM fails to start with:
{"component":"virt-launcher","level":"error","msg":"Failed to start VirtualMachineInstance",
"reason":"virError... vfio 0000:07:00.1: group 19 is not viable
Please ensure all devices within the iommu_group are bound to their vfio bus driver."}
Diagnosis: Not all devices in the IOMMU group are bound to vfio-pci. Check:
# Check devices in the IOMMU group
ls /sys/kernel/iommu_groups/19/devices/
# Check what these devices are
lspci -nnks 07:00.0
Solution: Bind all devices in the IOMMU group to vfio-pci:
# Add to vfio-prepare.sh
echo "vfio-pci" > /sys/bus/pci/devices/0000:03:08.0/driver_override
echo "vfio-pci" > /sys/bus/pci/devices/0000:07:00.0/driver_override
echo "vfio-pci" > /sys/bus/pci/devices/0000:07:00.1/driver_override
echo "vfio-pci" > /sys/bus/pci/devices/0000:07:00.3/driver_override
# Unbind from current drivers, then bind to vfio-pci
Common Issues and Solutions¶
No display output after GPU passthrough:
Disable virtual VGA in VM spec
Pass through both GPU and audio device
Install proper GPU drivers inside VM
Performance issues in Windows VM:
Configure CPU pinning correctly
Enable huge pages for better memory performance
Install latest NVIDIA drivers in VM
Disable Windows Game Bar and overlay software
GPU driver switching fails:
Stop all GPU workloads before switching
Check GPU operator logs:
oc logs -n nvidia-gpu-operator <pod-name>
Verify IOMMU is enabled in BIOS/UEFI
For further troubleshooting, check logs:
virt-handler:
oc logs -n openshift-cnv virt-handler-<hash>
virt-launcher:
oc logs -n <namespace> virt-launcher-<vm-name>-<hash>
nvidia-driver-daemonset:
oc logs -n nvidia-gpu-operator nvidia-driver-daemonset-<hash>