diff options
author | Simon Rettberg | 2023-07-26 17:41:31 +0200 |
---|---|---|
committer | Simon Rettberg | 2023-07-26 17:41:31 +0200 |
commit | c1e793128dc53cfed7faec60edf0c5998f527097 (patch) | |
tree | d40c0504139b93932af4956f9dfbc37b1dbf8b32 /core/modules | |
parent | [libvirt-python/libvirt-src] Bump libvirt version (diff) | |
download | mltk-c1e793128dc53cfed7faec60edf0c5998f527097.tar.gz mltk-c1e793128dc53cfed7faec60edf0c5998f527097.tar.xz mltk-c1e793128dc53cfed7faec60edf0c5998f527097.zip |
[qemu] java: Make pci passthrough generic, not just for nvidia
Diffstat (limited to 'core/modules')
-rw-r--r-- | core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/App.java | 6 | ||||
-rw-r--r-- | core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgs.java | 30 | ||||
-rw-r--r-- | core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuPciPassthrough.java (renamed from core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidia.java) | 121 | ||||
-rw-r--r-- | core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidiaTest.java | 10 |
4 files changed, 98 insertions, 69 deletions
diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/App.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/App.java index 6a0dc9cb..0744c9b5 100644 --- a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/App.java +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/App.java @@ -25,14 +25,14 @@ import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericInterf import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericMemory; import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericName; import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericParallelDevices; -import org.openslx.runvirt.plugin.qemu.configuration.TransformationSpecificQemuSerialDevices; import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericUuid; import org.openslx.runvirt.plugin.qemu.configuration.TransformationGenericWrapperScript; import org.openslx.runvirt.plugin.qemu.configuration.TransformationSpecificQemuArchitecture; import org.openslx.runvirt.plugin.qemu.configuration.TransformationSpecificQemuFirmware; -import org.openslx.runvirt.plugin.qemu.configuration.TransformationSpecificQemuGpuPassthroughNvidia; import org.openslx.runvirt.plugin.qemu.configuration.TransformationSpecificQemuGraphics; import org.openslx.runvirt.plugin.qemu.configuration.TransformationSpecificQemuMdevPassthroughIntel; +import org.openslx.runvirt.plugin.qemu.configuration.TransformationSpecificQemuPciPassthrough; +import org.openslx.runvirt.plugin.qemu.configuration.TransformationSpecificQemuSerialDevices; import org.openslx.runvirt.plugin.qemu.virtualization.LibvirtHypervisorQemu; import org.openslx.runvirt.plugin.qemu.virtualization.LibvirtHypervisorQemu.QemuSessionType; import org.openslx.runvirt.viewer.Viewer; @@ -160,7 +160,7 @@ public class App transformationManager.register( new TransformationSpecificQemuGraphics( hypervisorQemu ), true ); transformationManager.register( new TransformationSpecificQemuSerialDevices( hypervisorQemu ), true ); transformationManager.register( new TransformationSpecificQemuMdevPassthroughIntel( hypervisorQemu ), false ); - transformationManager.register( new TransformationSpecificQemuGpuPassthroughNvidia( hypervisorQemu ), false ); + transformationManager.register( new TransformationSpecificQemuPciPassthrough( hypervisorQemu ), false ); } // Needs to be last one since TransformationSpecificQemuArchitecture sets this too diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgs.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgs.java index 21b11968..396c0d8c 100644 --- a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgs.java +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/cmdln/CommandLineArgs.java @@ -13,6 +13,8 @@ import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.openslx.libvirt.domain.device.HostdevPciDeviceDescription; +import org.openslx.runvirt.plugin.qemu.configuration.TransformationSpecificQemuPciPassthrough; import org.openslx.util.Util; /** @@ -453,13 +455,33 @@ public class CommandLineArgs } /** - * Returns the state whether a passthrough of a NVIDIA GPU is required. - * - * @return state whether a passthrough of a NVIDIA GPU is required. + * Returns the state whether a passthrough of a NVIDIA GPU is requested. + * Do this by checking the vendor ID of each PCI device that's being passed + * through. If one of them is nvidia, assume we're running passthrough for + * an nvidia GPU. */ public boolean isNvidiaGpuPassthroughEnabled() { - return this.getVmNvGpuIds0().size() > 0; + List<String> pciIds = this.getVmNvGpuIds0(); + // parse PCI device description and PCI device address + for ( int i = 0; i < pciIds.size() - 1; i += 2 ) { + // parse vendor and device ID + HostdevPciDeviceDescription deviceDescription = null; + try { + deviceDescription = HostdevPciDeviceDescription.valueOf( pciIds.get( i ) ); + } catch ( IllegalArgumentException e ) { + continue; + } + + // validate vendor ID + final int vendorId = deviceDescription.getVendorId(); + if ( TransformationSpecificQemuPciPassthrough.NVIDIA_PCI_VENDOR_ID != vendorId ) + continue; + + // Have at least one device by nvidia, just assume it's a GPU for now + return true; + } + return false; } /** diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidia.java b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuPciPassthrough.java index 23e5fe18..1a20448f 100644 --- a/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidia.java +++ b/core/modules/qemu/runvirt-plugin-qemu/src/main/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuPciPassthrough.java @@ -6,14 +6,14 @@ import java.util.List; import org.openslx.libvirt.capabilities.Capabilities; import org.openslx.libvirt.domain.Domain; +import org.openslx.libvirt.domain.device.Device; +import org.openslx.libvirt.domain.device.Graphics.ListenType; import org.openslx.libvirt.domain.device.GraphicsSpice; import org.openslx.libvirt.domain.device.HostdevPci; import org.openslx.libvirt.domain.device.HostdevPciDeviceAddress; import org.openslx.libvirt.domain.device.HostdevPciDeviceDescription; import org.openslx.libvirt.domain.device.Shmem; import org.openslx.libvirt.domain.device.Video; -import org.openslx.libvirt.domain.device.Device; -import org.openslx.libvirt.domain.device.Graphics.ListenType; import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; import org.openslx.runvirt.plugin.qemu.virtualization.LibvirtHypervisorQemu; import org.openslx.runvirt.virtualization.LibvirtHypervisorException; @@ -26,7 +26,7 @@ import org.openslx.virtualization.configuration.transformation.TransformationSpe * @author Manuel Bentele * @version 1.0 */ -public class TransformationSpecificQemuGpuPassthroughNvidia +public class TransformationSpecificQemuPciPassthrough extends TransformationSpecific<Domain, CommandLineArgs, LibvirtHypervisorQemu> { /** @@ -37,7 +37,7 @@ public class TransformationSpecificQemuGpuPassthroughNvidia /** * Vendor identifier of PCI devices from Nvidia. */ - private static final int NVIDIA_PCI_VENDOR_ID = 0x10de; + public static final int NVIDIA_PCI_VENDOR_ID = 0x10de; /** * Switch to turn patch for Nvidia GPU-Passthrough (enables Hyper-V enlightening) on or off to @@ -71,9 +71,9 @@ public class TransformationSpecificQemuGpuPassthroughNvidia * * @param hypervisor Libvirt/QEMU hypervisor. */ - public TransformationSpecificQemuGpuPassthroughNvidia( LibvirtHypervisorQemu hypervisor ) + public TransformationSpecificQemuPciPassthrough( LibvirtHypervisorQemu hypervisor ) { - super( TransformationSpecificQemuGpuPassthroughNvidia.NAME, hypervisor ); + super( TransformationSpecificQemuPciPassthrough.NAME, hypervisor ); } /** @@ -115,13 +115,6 @@ public class TransformationSpecificQemuGpuPassthroughNvidia throw new TransformationException( "Invalid vendor or device ID of the PCI device description!" ); } - // validate vendor ID - final int vendorId = deviceDescription.getVendorId(); - if ( TransformationSpecificQemuGpuPassthroughNvidia.NVIDIA_PCI_VENDOR_ID != vendorId ) { - final String errorMsg = "Vendor ID '" + vendorId + "' of the PCI device is not from Nvidia!"; - throw new TransformationException( errorMsg ); - } - // parse PCI domain, PCI bus, PCI device and PCI function final HostdevPciDeviceAddress parsedPciAddress = HostdevPciDeviceAddress.valueOf( pciIds.get( i + 1 ) ); if ( parsedPciAddress != null ) { @@ -146,7 +139,7 @@ public class TransformationSpecificQemuGpuPassthroughNvidia throw new TransformationException( "Virtualization configuration or input arguments are missing!" ); } - TransformationSpecificQemuGpuPassthroughNvidia.validateParseNvidiaPciIds( args.getVmNvGpuIds0() ); + TransformationSpecificQemuPciPassthrough.validateParseNvidiaPciIds( args.getVmNvGpuIds0() ); } /** @@ -202,58 +195,53 @@ public class TransformationSpecificQemuGpuPassthroughNvidia // validate configuration and input arguments this.validateInputs( config, args ); - // check if passthrough of Nvidia GPU takes place - if ( args.isNvidiaGpuPassthroughEnabled() ) { - // validate submitted PCI IDs - final List<HostdevPciDeviceAddress> pciDeviceAddresses = TransformationSpecificQemuGpuPassthroughNvidia - .validateParseNvidiaPciIds( args.getVmNvGpuIds0() ); - - // check if IOMMU support is available on the host - if ( !this.getCapabilities().hasHostIommuSupport() ) { - final String errorMsg = "IOMMU support is not available on the hypervisor but required for GPU passthrough!"; - throw new TransformationException( errorMsg ); - } - // Check config for PCI addresses already in use - boolean inUse[] = new boolean[ 64 ]; - inUse[0] = true; - inUse[1] = true; - for ( Device dev : config.getDevices() ) { - HostdevPciDeviceAddress target = dev.getPciTarget(); - if ( target == null ) - continue; - if ( target.getPciDomain() != 0 || target.getPciBus() != 0 ) - continue; // Ignore non-primary bus - if ( target.getPciDevice() >= inUse.length ) - continue; - inUse[target.getPciDevice()] = true; - } - // Use first free one. Usually 00:02:00 is primary VGA - int devAddr; - for ( devAddr = 0; devAddr < inUse.length; ++devAddr ) { - if ( !inUse[devAddr] ) - break; - } + // validate submitted PCI IDs + final List<HostdevPciDeviceAddress> pciDeviceAddresses = TransformationSpecificQemuPciPassthrough + .validateParseNvidiaPciIds( args.getVmNvGpuIds0() ); - // passthrough PCI devices of the GPU - for ( final HostdevPciDeviceAddress pciDeviceAddress : pciDeviceAddresses ) { - final HostdevPci pciDevice = config.addHostdevPciDevice(); - pciDevice.setManaged( true ); - pciDevice.setSource( pciDeviceAddress ); - if ( pciDeviceAddress.getPciFunction() == 0 && pciDeviceAddresses.size() > 1 ) { - pciDevice.setMultifunction( true ); - } - pciDevice.setPciTarget( new HostdevPciDeviceAddress( 0, devAddr, pciDeviceAddress.getPciFunction() ) ); + // check if IOMMU support is available on the host + if ( !this.getCapabilities().hasHostIommuSupport() ) { + final String errorMsg = "IOMMU support is not available on the hypervisor but required for GPU passthrough!"; + throw new TransformationException( errorMsg ); + } + // Check config for PCI addresses already in use + int inUse[] = new int[ 64 ]; + inUse[0] = Integer.MAX_VALUE; + inUse[1] = Integer.MAX_VALUE; + for ( Device dev : config.getDevices() ) { + HostdevPciDeviceAddress target = dev.getPciTarget(); + if ( target == null ) + continue; + if ( target.getPciDomain() != 0 || target.getPciBus() != 0 ) + continue; // Ignore non-primary bus + if ( target.getPciDevice() >= inUse.length ) + continue; + inUse[target.getPciDevice()] = Integer.MAX_VALUE; + } + + // passthrough PCI devices of the GPU + for ( final HostdevPciDeviceAddress pciDeviceAddress : pciDeviceAddresses ) { + final HostdevPci pciDevice = config.addHostdevPciDevice(); + pciDevice.setManaged( true ); + pciDevice.setSource( pciDeviceAddress ); + if ( pciDeviceAddress.getPciFunction() == 0 && pciDeviceAddresses.size() > 1 ) { + pciDevice.setMultifunction( true ); } + int devAddr = getFreeAddr( inUse, pciDeviceAddress ); + pciDevice.setPciTarget( new HostdevPciDeviceAddress( 0, devAddr, pciDeviceAddress.getPciFunction() ) ); + } + // check if passthrough of Nvidia GPU takes place + if ( args.isNvidiaGpuPassthroughEnabled() ) { // add shared memory device for Looking Glass final Shmem shmemDevice = config.addShmemDevice(); shmemDevice.setName( "looking-glass" ); shmemDevice.setModel( Shmem.Model.IVSHMEM_PLAIN ); - shmemDevice.setSize( TransformationSpecificQemuGpuPassthroughNvidia.calculateFramebufferSize() ); + shmemDevice.setSize( TransformationSpecificQemuPciPassthrough.calculateFramebufferSize() ); // enable hypervisor shadowing to avoid error code 43 of Nvidia drivers in virtual machines - if ( TransformationSpecificQemuGpuPassthroughNvidia.NVIDIA_PATCH ) { - config.setFeatureHypervVendorIdValue( TransformationSpecificQemuGpuPassthroughNvidia.HYPERV_VENDOR_ID ); + if ( TransformationSpecificQemuPciPassthrough.NVIDIA_PATCH ) { + config.setFeatureHypervVendorIdValue( TransformationSpecificQemuPciPassthrough.HYPERV_VENDOR_ID ); config.setFeatureHypervVendorIdState( true ); config.setFeatureKvmHiddenState( true ); } @@ -274,4 +262,23 @@ public class TransformationSpecificQemuGpuPassthroughNvidia } } } + + private int getFreeAddr( int[] inUse, HostdevPciDeviceAddress pciDeviceAddress ) + { + // Use first free one. Usually 00:02:00 is primary VGA + int devAddr; + int firstFree = -1; + int lookup = (pciDeviceAddress.getPciDomain() << 16) + | (pciDeviceAddress.getPciBus() << 8) + | (pciDeviceAddress.getPciDevice()); + for ( devAddr = 0; devAddr < inUse.length; ++devAddr ) { + if ( firstFree == -1 && inUse[devAddr] == 0 ) { + firstFree = devAddr; + } else if ( inUse[devAddr] == lookup ) { + return devAddr; + } + } + inUse[firstFree] = lookup; + return firstFree; + } } diff --git a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidiaTest.java b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidiaTest.java index 4c021363..ae9f531b 100644 --- a/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidiaTest.java +++ b/core/modules/qemu/runvirt-plugin-qemu/src/test/java/org/openslx/runvirt/plugin/qemu/configuration/TransformationSpecificQemuGpuPassthroughNvidiaTest.java @@ -29,7 +29,7 @@ import org.openslx.libvirt.xml.LibvirtXmlValidationException; import org.openslx.runvirt.plugin.qemu.cmdln.CommandLineArgs; import org.openslx.virtualization.configuration.transformation.TransformationException; -class TransformationSpecificQemuGpuPassthroughNvidiaStub extends TransformationSpecificQemuGpuPassthroughNvidia +class TransformationSpecificQemuGpuPassthroughNvidiaStub extends TransformationSpecificQemuPciPassthrough { final String capabilityFileName; @@ -87,13 +87,13 @@ public class TransformationSpecificQemuGpuPassthroughNvidiaTest assertEquals( Shmem.Model.IVSHMEM_PLAIN, shmemDevice.getModel() ); assertEquals( BigInteger.valueOf( 67108864 ).toString(), shmemDevice.getSize().toString() ); - if ( TransformationSpecificQemuGpuPassthroughNvidia.NVIDIA_PATCH ) { - assertEquals( TransformationSpecificQemuGpuPassthroughNvidia.HYPERV_VENDOR_ID, + if ( TransformationSpecificQemuPciPassthrough.NVIDIA_PATCH ) { + assertEquals( TransformationSpecificQemuPciPassthrough.HYPERV_VENDOR_ID, config.getFeatureHypervVendorIdValue() ); assertTrue( config.isFeatureHypervVendorIdStateOn() ); assertTrue( config.isFeatureKvmHiddenStateOn() ); } else { - assertNotEquals( TransformationSpecificQemuGpuPassthroughNvidia.HYPERV_VENDOR_ID, + assertNotEquals( TransformationSpecificQemuPciPassthrough.HYPERV_VENDOR_ID, config.getFeatureHypervVendorIdValue() ); assertFalse( config.isFeatureHypervVendorIdStateOn() ); assertFalse( config.isFeatureKvmHiddenStateOn() ); @@ -136,7 +136,7 @@ public class TransformationSpecificQemuGpuPassthroughNvidiaTest assertNotNull( shmemDevices ); assertEquals( 0, shmemDevices.size() ); - assertNotEquals( TransformationSpecificQemuGpuPassthroughNvidia.HYPERV_VENDOR_ID, + assertNotEquals( TransformationSpecificQemuPciPassthrough.HYPERV_VENDOR_ID, config.getFeatureHypervVendorIdValue() ); assertFalse( config.isFeatureHypervVendorIdStateOn() ); assertFalse( config.isFeatureKvmHiddenStateOn() ); |