Performance tuning and anti-detection techniques for KVM virtual machines, including CPU pinning, multithreading, hypervisor spoofing, and CPUID faulting.
Table of Contents#
- Overview
- CPU Pinning
- AMD CPU Multithreading
- Hypervisor Spoofing
- Kernel Parameter Spoofing
- Troubleshooting
- See Also
- Sources
1. Overview#
KVM virtual machines benefit significantly from tuning, especially for GPU passthrough and gaming workloads. Key areas include:
- CPU pinning - assigns specific host threads to VM vCPUs, reducing scheduling jitter
- Multithreading - exposes correct CPU topology to the guest (required for AMD SMT)
- Spoofing - hides the hypervisor from anti-cheat and DRM software
- CPUID faulting - kernel-level interception of virtualization detection instructions
2. CPU Pinning#
CPU pinning tells the hypervisor which threads should be prioritized for the VM and not shared between the host and guest. This does not isolate the threads from the host system; for full isolation, see the Arch Wiki on isolating pinned CPUs.
Use lscpu -e to check which core belongs to which CPU block. For background on CPU topology, see the Arch Wiki on CPU topology.
Example: Ryzen 9 7950X (pin first 16 threads, allow all 32)#
<vcpu placement="static">32</vcpu>
<iothreads>1</iothreads>
<cputune>
<!-- Pin vCPUs 0-15 to host CPUs 0-15 (first CCD) -->
<vcpupin vcpu="0" cpuset="0"/>
<vcpupin vcpu="1" cpuset="1"/>
<vcpupin vcpu="2" cpuset="2"/>
<vcpupin vcpu="3" cpuset="3"/>
<vcpupin vcpu="4" cpuset="4"/>
<vcpupin vcpu="5" cpuset="5"/>
<vcpupin vcpu="6" cpuset="6"/>
<vcpupin vcpu="7" cpuset="7"/>
<vcpupin vcpu="8" cpuset="8"/>
<vcpupin vcpu="9" cpuset="9"/>
<vcpupin vcpu="10" cpuset="10"/>
<vcpupin vcpu="11" cpuset="11"/>
<vcpupin vcpu="12" cpuset="12"/>
<vcpupin vcpu="13" cpuset="13"/>
<vcpupin vcpu="14" cpuset="14"/>
<vcpupin vcpu="15" cpuset="15"/>
<!-- Emulator and IO thread pinned to first two cores -->
<emulatorpin cpuset="0-1"/>
<iothreadpin iothread="1" cpuset="0-1"/>
</cputune>Tip: Always match
vcpupinmappings to your actual topology fromlscpu -e. On AMD Zen processors, keep pinned cores within the same CCD/CCX for optimal L3 cache sharing.
3. AMD CPU Multithreading#
To enable multithreading (SMT) on AMD CPUs within a KVM guest, the topoext feature must be exposed. This allows the guest to see the correct thread-per-core topology.
<cpu mode='host-passthrough' check='none' migratable="on">
<cache mode="passthrough"/>
<!-- Invariant TSC for stable timekeeping -->
<feature policy="require" name="invtsc"/>
<!-- Required for AMD SMT topology exposure -->
<feature policy='require' name='topoext'/>
</cpu>Note:
host-passthroughforwards the host CPU model directly to the guest, providing the best performance but preventing live migration to hosts with different CPUs.
4. Hypervisor Spoofing#
To hide the fact that the machine is virtualized (useful for anti-cheat software, DRM, or GPU driver restrictions), adjust the libvirt XML configuration:
SMBIOS and Hyper-V Enlightenments#
<os firmware="efi">
<!-- Pass host SMBIOS data to the guest -->
<smbios mode="host"/>
</os>
<features>
<hyperv mode="custom">
<!-- Performance enlightenments -->
<relaxed state="on"/>
<vapic state="on"/>
<spinlocks state="on" retries="8191"/>
<vpindex state="on"/>
<runtime state="on"/>
<synic state="on"/>
<stimer state="on"/>
<reset state="on"/>
<!-- Custom vendor ID hides Hyper-V from detection -->
<vendor_id state="on" value="FckYouBudge"/>
<frequencies state="on"/>
</hyperv>
<kvm>
<!-- Hide KVM signature from CPUID -->
<hidden state="on"/>
</kvm>
</features>CPU Feature and Clock Configuration#
<cpu>
<!-- Remove the hypervisor CPUID bit -->
<feature policy="disable" name="hypervisor"/>
</cpu>
<clock offset="utc">
<timer name="pit" tickpolicy="delay"/>
<timer name="rtc" tickpolicy="catchup" track="guest"/>
<timer name="hpet" present="no"/>
<!-- Native TSC mode for accurate timing -->
<timer name="tsc" present="yes" mode="native"/>
<!-- Hyper-V reference clock for Windows guests -->
<timer name="hypervclock" present="yes"/>
</clock>5. Kernel Parameter Spoofing#
CPUID faulting allows a hypervisor to intercept and handle certain CPUID instructions that guest operating systems use to detect virtualization. By intercepting these instructions, the hypervisor can hide information about the underlying hardware from the guest.
Add clearcpuid=514 to your kernel command line parameters.
GRUB#
Edit /etc/default/grub and append the parameter to GRUB_CMDLINE_LINUX_DEFAULT:
GRUB_CMDLINE_LINUX_DEFAULT="quiet clearcpuid=514"Then regenerate the GRUB configuration:
# Arch Linux, Manjaro
sudo grub-mkconfig -o /boot/grub/grub.cfg
# Debian, Ubuntu
sudo update-grub
# Fedora, RHEL
sudo grub2-mkconfig -o /boot/grub2/grub.cfgWarning: The grub regeneration command differs by distribution and boot setup (BIOS vs UEFI). Always verify the correct output path for your system before running.
systemd-boot#
For systems using systemd-boot, edit the relevant entry in /boot/loader/entries/ and append clearcpuid=514 to the options line.
Troubleshooting#
| Issue | Cause | Solution |
|---|---|---|
| VM performance worse after CPU pinning | Pins overlap with host-critical threads (e.g., IRQ handling) | Use lscpu -e to verify topology; pin emulator to separate cores from vCPUs |
| Guest does not see SMT/hyperthreading | Missing topoext feature on AMD | Add <feature policy='require' name='topoext'/> to CPU XML |
| Anti-cheat still detects VM | Incomplete spoofing configuration | Ensure all sections (hyperv, kvm hidden, CPUID, clock) are applied together |
| Windows guest BSOD after spoofing changes | SMM or Hyper-V enlightenment conflict | Verify <smm state="on"/> is set; test enlightenments one at a time |
clearcpuid=514 has no effect | Parameter not applied to active kernel | Verify with cat /proc/cmdline; ensure grub config was regenerated correctly |