diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java b/api/src/main/java/com/cloud/vm/VmDetailConstants.java index dd0a5d754bb..add2518321b 100644 --- a/api/src/main/java/com/cloud/vm/VmDetailConstants.java +++ b/api/src/main/java/com/cloud/vm/VmDetailConstants.java @@ -48,6 +48,10 @@ public interface VmDetailConstants { String IOTHREADS = "iothreads"; String IO_POLICY = "io.policy"; + // KVM specific, the number of queues for multiqueue NICs + String NIC_MULTIQUEUE_NUMBER = "nic.multiqueue.number"; + String NIC_PACKED_VIRTQUEUES_ENABLED = "nic.packed.virtqueues.enabled"; + // Mac OSX guest specific (internal) String SMC_PRESENT = "smc.present"; String FIRMWARE = "firmware"; diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index c1438251b85..76c0830527f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -299,6 +299,8 @@ public class ApiConstants { public static final String NIC = "nic"; public static final String NIC_NETWORK_LIST = "nicnetworklist"; public static final String NIC_IP_ADDRESS_LIST = "nicipaddresslist"; + public static final String NIC_MULTIQUEUE_NUMBER = "nicmultiqueuenumber"; + public static final String NIC_PACKED_VIRTQUEUES_ENABLED = "nicpackedvirtqueuesenabled"; public static final String NEW_START_IP = "newstartip"; public static final String NEW_END_IP = "newendip"; public static final String NUM_RETRIES = "numretries"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index 628b332cecb..17eb92854a1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -264,6 +264,14 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG @Parameter(name = ApiConstants.IO_DRIVER_POLICY, type = CommandType.STRING, description = "Controls specific policies on IO") private String ioDriverPolicy; + @Parameter(name = ApiConstants.NIC_MULTIQUEUE_NUMBER, type = CommandType.INTEGER, since = "4.18", + description = "The number of queues for multiqueue NICs.") + private Integer nicMultiqueueNumber; + + @Parameter(name = ApiConstants.NIC_PACKED_VIRTQUEUES_ENABLED, type = CommandType.BOOLEAN, since = "4.18", + description = "Enable packed virtqueues or not.") + private Boolean nicPackedVirtQueues; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -338,6 +346,14 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG customparameterMap.put(VmDetailConstants.IOTHREADS, BooleanUtils.toStringTrueFalse(iothreadsEnabled)); } + if (nicMultiqueueNumber != null) { + customparameterMap.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, nicMultiqueueNumber.toString()); + } + + if (BooleanUtils.toBoolean(nicPackedVirtQueues)) { + customparameterMap.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, BooleanUtils.toStringTrueFalse(nicPackedVirtQueues)); + } + return customparameterMap; } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 2ac6da86dc6..f4ce29c5762 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -2843,7 +2843,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv for (int i = 0; i < nics.length; i++) { for (final NicTO nic : vmSpec.getNics()) { if (nic.getDeviceId() == i) { - createVif(vm, nic, nicAdapter, extraConfig); + createVif(vm, vmSpec, nic, nicAdapter, extraConfig); } } } @@ -3224,12 +3224,16 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } } - private void createVif(final LibvirtVMDef vm, final NicTO nic, final String nicAdapter, Map extraConfig) throws InternalErrorException, LibvirtException { + private void createVif(final LibvirtVMDef vm, final VirtualMachineTO vmSpec, final NicTO nic, final String nicAdapter, Map extraConfig) throws InternalErrorException, LibvirtException { if (vm.getDevices() == null) { s_logger.error("LibvirtVMDef object get devices with null result"); throw new InternalErrorException("LibvirtVMDef object get devices with null result"); } - vm.getDevices().addDevice(getVifDriver(nic.getType(), nic.getName()).plug(nic, vm.getPlatformEmulator(), nicAdapter, extraConfig)); + final InterfaceDef interfaceDef = getVifDriver(nic.getType(), nic.getName()).plug(nic, vm.getPlatformEmulator(), nicAdapter, extraConfig); + if (vmSpec.getDetails() != null) { + setInterfaceDefQueueSettings(vmSpec.getDetails(), vmSpec.getCpus(), interfaceDef); + } + vm.getDevices().addDevice(interfaceDef); } public boolean cleanupDisk(Map volumeToDisconnect) { @@ -5143,4 +5147,30 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv public static String generateSecretUUIDFromString(String seed) { return UUID.nameUUIDFromBytes(seed.getBytes()).toString(); } + + public void setInterfaceDefQueueSettings(Map details, Integer cpus, InterfaceDef interfaceDef) { + String nicMultiqueueNumber = details.get(VmDetailConstants.NIC_MULTIQUEUE_NUMBER); + if (nicMultiqueueNumber != null) { + try { + Integer nicMultiqueueNumberInteger = Integer.valueOf(nicMultiqueueNumber); + if (nicMultiqueueNumberInteger == InterfaceDef.MULTI_QUEUE_NUMBER_MEANS_CPU_CORES) { + if (cpus != null) { + interfaceDef.setMultiQueueNumber(cpus); + } + } else { + interfaceDef.setMultiQueueNumber(nicMultiqueueNumberInteger); + } + } catch (NumberFormatException ex) { + s_logger.warn(String.format("VM details %s is not a valid integer value %s", VmDetailConstants.NIC_MULTIQUEUE_NUMBER, nicMultiqueueNumber)); + } + } + String nicPackedEnabled = details.get(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED); + if (nicPackedEnabled != null) { + try { + interfaceDef.setPackedVirtQueues(Boolean.valueOf(nicPackedEnabled)); + } catch (NumberFormatException ex) { + s_logger.warn(String.format("VM details %s is not a valid Boolean value %s", VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, nicPackedEnabled)); + } + } + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java index cb650e24b2f..9a27e5e4322 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java @@ -249,6 +249,15 @@ public class LibvirtDomainXMLParser { def.setDpdkOvsPath(ovsPath); def.setInterfaceMode(mode); } + String multiQueueNumber = getAttrValue("driver", "queues", nic); + if (StringUtils.isNotBlank(multiQueueNumber)) { + def.setMultiQueueNumber(Integer.valueOf(multiQueueNumber)); + } + + String packedOn = getAttrValue("driver", "packed", nic); + if (StringUtils.isNotBlank(packedOn)) { + def.setPackedVirtQueues("on".equalsIgnoreCase(packedOn)); + } if (StringUtils.isNotBlank(slot)) { def.setSlot(Integer.parseInt(slot, 16)); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java index b739c0ee0ab..aac44fc1419 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java @@ -1280,6 +1280,8 @@ public class LibvirtVMDef { DIRECT_ATTACHED_WITHOUT_DHCP, DIRECT_ATTACHED_WITH_DHCP, VNET, VLAN; } + public static final int MULTI_QUEUE_NUMBER_MEANS_CPU_CORES = -1; + private GuestNetType _netType; /* * bridge, ethernet, network, user, * internal, vhostuser @@ -1305,6 +1307,8 @@ public class LibvirtVMDef { private String _interfaceMode; private String _userIp4Network; private Integer _userIp4Prefix; + private Integer _multiQueueNumber; + private Boolean _packedVirtQueues; public void defBridgeNet(String brName, String targetBrName, String macAddr, NicModel model) { defBridgeNet(brName, targetBrName, macAddr, model, 0); @@ -1493,6 +1497,14 @@ public class LibvirtVMDef { _interfaceMode = mode; } + public void setMultiQueueNumber(Integer multiQueueNumber) { + this._multiQueueNumber = multiQueueNumber; + } + + public void setPackedVirtQueues(Boolean packedVirtQueues) { + this._packedVirtQueues = packedVirtQueues; + } + public String getContent() { StringBuilder netBuilder = new StringBuilder(); if (_netType == GuestNetType.BRIDGE) { @@ -1515,6 +1527,21 @@ public class LibvirtVMDef { if (_model != null) { netBuilder.append("\n"); } + if (NicModel.VIRTIO.equals(_model)) { + boolean isMultiQueueNumberSpecified = _multiQueueNumber != null; + boolean isPackedVirtQueuesEnabled = _packedVirtQueues != null && _packedVirtQueues + && s_qemuVersion >= 4200000 && s_libvirtVersion >= 6300000; + if (isMultiQueueNumberSpecified || isPackedVirtQueuesEnabled) { + netBuilder.append("\n"); + } + } if ((s_libvirtVersion >= 9004) && (_networkRateKBps > 0)) { // supported from libvirt 0.9.4 netBuilder.append("\n"); netBuilder.append("\n"); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java index ca78c718886..dffa8360c20 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java @@ -64,6 +64,9 @@ public final class LibvirtPlugNicCommandWrapper extends CommandWrapper\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; + assertEquals(expected, ifDef.toString()); + + ifDef.setPackedVirtQueues(true); + expected = + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; + assertEquals(expected, ifDef.toString()); + + ifDef.setMultiQueueNumber(null); + expected = + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; + assertEquals(expected, ifDef.toString()); + + LibvirtVMDef.setGlobalLibvirtVersion(300000L); + expected = + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; + assertEquals(expected, ifDef.toString()); + } + @Test public void testCpuModeDef() { LibvirtVMDef.CpuModeDef cpuModeDef = new LibvirtVMDef.CpuModeDef(); diff --git a/scripts/vm/network/tungsten/create_tap_device.sh b/scripts/vm/network/tungsten/create_tap_device.sh index c10a0e72105..90c24d62b3a 100755 --- a/scripts/vm/network/tungsten/create_tap_device.sh +++ b/scripts/vm/network/tungsten/create_tap_device.sh @@ -16,5 +16,5 @@ # specific language governing permissions and limitations # under the License. -ip tuntap add dev $1 mode tap -ip link set $1 up \ No newline at end of file +#ip tuntap add dev $1 mode tap multi_queue +#ip link set $1 up diff --git a/scripts/vm/network/tungsten/delete_tap_device.sh b/scripts/vm/network/tungsten/delete_tap_device.sh index 37d668af8af..8a3677236e2 100755 --- a/scripts/vm/network/tungsten/delete_tap_device.sh +++ b/scripts/vm/network/tungsten/delete_tap_device.sh @@ -16,4 +16,7 @@ # specific language governing permissions and limitations # under the License. -ip tuntap del dev $1 mode tap \ No newline at end of file +ip tuntap del dev $1 mode tap multi_queue 2>/dev/null +if [ $? -ne 0 ];then + ip tuntap del dev $1 mode tap +fi diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index ce5b9db574d..e78f6958e1a 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -4053,6 +4053,8 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q options.put(VmDetailConstants.VIDEO_RAM, Collections.emptyList()); options.put(VmDetailConstants.IO_POLICY, Arrays.asList("threads", "native", "io_uring", "storage_specific")); options.put(VmDetailConstants.IOTHREADS, Arrays.asList("enabled")); + options.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, Collections.emptyList()); + options.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, Arrays.asList("true", "false")); } if (HypervisorType.VMware.equals(hypervisorType)) { diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 5284a3b1379..cedb3cc930e 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1337,6 +1337,10 @@ "label.nfsserver": "NFS server", "label.nic": "NIC", "label.nicadaptertype": "NIC adapter type", +"label.nicmultiqueuenumber" : "NIC multiqueue number", +"label.nicmultiqueuenumber.tooltip" : "NIC multiqueue number. This supports only KVM. The value \"-1\" indicates the NIC multiqueue number will be set to the vCPU number of the instance.", +"label.nicpackedvirtqueuesenabled" : "NIC packed virtqueues enabled", +"label.nicpackedvirtqueuesenabled.tooltip" : "Enable NIC packed virtqueues or not. This supports only KVM with QEMU >= 4.2.0 and Libvirt >=6.3.0.", "label.nics": "NICs", "label.no": "No", "label.no.data": "No data to show", diff --git a/ui/src/views/compute/DeployVM.vue b/ui/src/views/compute/DeployVM.vue index 064dac21e79..d635d3f8e8c 100644 --- a/ui/src/views/compute/DeployVM.vue +++ b/ui/src/views/compute/DeployVM.vue @@ -697,6 +697,23 @@ @select-affinity-group-item="($event) => updateAffinityGroups($event)" @handle-search-filter="($event) => handleSearchFilter('affinityGroups', $event)"/> + + + + + + + +