You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by da...@apache.org on 2023/01/25 11:34:13 UTC

[cloudstack] branch main updated: KVM support of iothreads and IO driver policy (#6909)

This is an automated email from the ASF dual-hosted git repository.

dahn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new d288bb0c78e KVM support of iothreads and IO driver policy (#6909)
d288bb0c78e is described below

commit d288bb0c78e0f3964982926070bd14785c88a441
Author: slavkap <51...@users.noreply.github.com>
AuthorDate: Wed Jan 25 13:34:05 2023 +0200

    KVM support of iothreads and IO driver policy (#6909)
---
 agent/conf/agent.properties                        |  6 +-
 .../cloud/agent/properties/AgentProperties.java    | 14 ++---
 .../main/java/com/cloud/vm/VmDetailConstants.java  |  4 ++
 .../org/apache/cloudstack/api/ApiConstants.java    | 25 ++++++++
 .../api/command/user/vm/DeployVMCmd.java           | 32 ++++++++++
 .../java/com/cloud/storage/StorageManager.java     |  9 +++
 .../engine/orchestration/VolumeOrchestrator.java   | 19 ++++++
 .../kvm/resource/LibvirtComputingResource.java     | 66 ++++++++++++++------
 .../hypervisor/kvm/resource/LibvirtVMDef.java      | 70 ++++++++++++++--------
 .../kvm/storage/KVMStorageProcessor.java           | 20 +++++--
 .../kvm/resource/LibvirtComputingResourceTest.java | 14 +++--
 .../java/com/cloud/api/query/QueryManagerImpl.java |  2 +
 .../java/com/cloud/storage/StorageManagerImpl.java |  1 +
 .../com/cloud/storage/VolumeApiServiceImpl.java    | 30 +++++++++-
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  | 11 ++++
 ui/public/locales/en.json                          |  4 ++
 ui/src/views/compute/DeployVM.vue                  | 38 +++++++++++-
 17 files changed, 297 insertions(+), 68 deletions(-)

diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties
index 29841584595..27c0e387fa3 100644
--- a/agent/conf/agent.properties
+++ b/agent/conf/agent.properties
@@ -309,9 +309,6 @@ iscsi.session.cleanup.enabled=false
 # Manually sets the host CPU MHz, in cases where CPU scaling support detects the value is wrong.
 #host.cpu.manual.speed.mhz=0
 
-# Enables/disables the IO driver for Qemu (in case it is not set CloudStack can also detect if it is supported by Qemu).
-#enable.io.uring=
-
 # Defines the location for Hypervisor scripts.
 # The path defined in this property is relative.
 # To locate the script, ACS first tries to concatenate
@@ -398,3 +395,6 @@ iscsi.session.cleanup.enabled=false
 #consoleproxy.httpListenPort=443
 
 #ping.retries=5
+
+# The number of iothreads. There should be only 1 or 2 IOThreads per VM CPU (default is 1). The recommended number of iothreads is 1
+# iothreads=1
diff --git a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
index fa97e6a2723..9a031e001fa 100644
--- a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
+++ b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
@@ -561,13 +561,6 @@ public class AgentProperties{
      */
     public static final Property<Integer> HOST_CPU_MANUAL_SPEED_MHZ = new Property<>("host.cpu.manual.speed.mhz", 0);
 
-    /**
-     * Enable manually IO driver on KVM's VM. <br>
-     * Data type: Boolean.<br>
-     * Default value: <code>null</code>
-     */
-    public static final Property<Boolean> ENABLE_IO_URING = new Property<>("enable.io.uring", null, Boolean.class);
-
     /**
      * Defines the location for Hypervisor scripts.<br>
      * The path defined in this property is relative.<br>
@@ -721,6 +714,13 @@ public class AgentProperties{
      */
     public static final Property<Integer> VM_MEMBALLOON_STATS_PERIOD = new Property<>("vm.memballoon.stats.period", 0);
 
+    /**
+     * The number of iothreads
+     * Data type: Integer.<br>
+     * Default value: <code>1</code>
+     */
+    public static final Property<Integer> IOTHREADS = new Property<>("iothreads", 1);
+
     public static class Property <T>{
         private String name;
         private T defaultValue;
diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java b/api/src/main/java/com/cloud/vm/VmDetailConstants.java
index 45dc7a2946f..dd0a5d754bb 100644
--- a/api/src/main/java/com/cloud/vm/VmDetailConstants.java
+++ b/api/src/main/java/com/cloud/vm/VmDetailConstants.java
@@ -44,6 +44,10 @@ public interface VmDetailConstants {
     String VIDEO_HARDWARE = "video.hardware";
     String VIDEO_RAM = "video.ram";
 
+    //KVM specific, loop threads for block IO requests
+    String IOTHREADS = "iothreads";
+    String IO_POLICY = "io.policy";
+
     // 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 3cb937e8427..99e19ab4288 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -922,6 +922,8 @@ public class ApiConstants {
     public static final String TEMPLATETYPE = "templatetype";
     public static final String SOURCETEMPLATEID = "sourcetemplateid";
     public static final String DYNAMIC_SCALING_ENABLED = "dynamicscalingenabled";
+    public static final String IOTHREADS_ENABLED = "iothreadsenabled";
+    public static final String IO_DRIVER_POLICY = "iodriverpolicy";
 
     public static final String POOL_TYPE = "pooltype";
     public static final String REDUNDANT_STATE = "redundantstate";
@@ -935,6 +937,29 @@ public class ApiConstants {
     public static final String PUBLIC_MTU = "publicmtu";
     public static final String PRIVATE_MTU = "privatemtu";
     public static final String MTU = "mtu";
+
+    /**
+     * This enum specifies IO Drivers, each option controls specific policies on I/O.
+     * Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0).
+     */
+    public enum IoDriverPolicy {
+        NATIVE("native"),
+        THREADS("threads"),
+        IO_URING("io_uring"),
+        STORAGE_SPECIFIC("storage_specific");
+
+        String ioDriver;
+
+        IoDriverPolicy(String driver) {
+            ioDriver = driver;
+        }
+
+        @Override
+        public String toString() {
+            return ioDriver;
+        }
+    }
+
     public enum BootType {
         UEFI, BIOS;
 
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 8a89a7d81cb..628b332cecb 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
@@ -33,6 +33,7 @@ import org.apache.cloudstack.api.ACL;
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiCommandResourceType;
 import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy;
 import org.apache.cloudstack.api.ApiErrorCode;
 import org.apache.cloudstack.api.BaseAsyncCreateCustomIdCmd;
 import org.apache.cloudstack.api.Parameter;
@@ -73,6 +74,8 @@ import com.cloud.utils.net.Dhcp;
 import com.cloud.utils.net.NetUtils;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VmDetailConstants;
+
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 
 @APICommand(name = "deployVirtualMachine", description = "Creates and automatically starts a virtual machine based on a service offering, disk offering, and template.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class},
@@ -254,6 +257,13 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG
             "In case of virtual machine deploying from ISO, then the diskofferingid specified for root volume is ignored and uses this override disk offering id")
     private Long overrideDiskOfferingId;
 
+    @Parameter(name = ApiConstants.IOTHREADS_ENABLED, type = CommandType.BOOLEAN, required = false,
+            description = "IOThreads are dedicated event loop threads for supported disk devices to perform block I/O requests in order to improve scalability especially on an SMP host/guest with many LUNs.")
+    private Boolean iothreadsEnabled;
+
+    @Parameter(name = ApiConstants.IO_DRIVER_POLICY, type = CommandType.STRING, description = "Controls specific policies on IO")
+    private String ioDriverPolicy;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -319,6 +329,15 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG
             customparameterMap.put("rootdisksize", rootdisksize.toString());
         }
 
+        IoDriverPolicy ioPolicy = getIoDriverPolicy();
+        if (ioPolicy != null) {
+            customparameterMap.put(VmDetailConstants.IO_POLICY, ioPolicy.toString());
+        }
+
+        if (BooleanUtils.toBoolean(iothreadsEnabled)) {
+            customparameterMap.put(VmDetailConstants.IOTHREADS, BooleanUtils.toStringTrueFalse(iothreadsEnabled));
+        }
+
         return customparameterMap;
     }
 
@@ -682,6 +701,19 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG
         return overrideDiskOfferingId;
     }
 
+    public ApiConstants.IoDriverPolicy getIoDriverPolicy() {
+        if (StringUtils.isNotBlank(ioDriverPolicy)) {
+            try {
+                String policyType = ioDriverPolicy.trim().toUpperCase();
+                return ApiConstants.IoDriverPolicy.valueOf(policyType);
+            } catch (IllegalArgumentException e) {
+                String errMesg = String.format("Invalid io policy %s specified for vm %s. Valid values are: %s", ioDriverPolicy, getName(), Arrays.toString(ApiConstants.IoDriverPolicy.values()));
+                s_logger.warn(errMesg);
+                throw new InvalidParameterValueException(errMesg);
+            }
+        }
+        return null;
+    }
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java
index b256d8fb86e..e332858e787 100644
--- a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java
+++ b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java
@@ -137,6 +137,15 @@ public interface StorageManager extends StorageService {
             ConfigKey.Scope.StoragePool,
             null);
 
+    ConfigKey<String> STORAGE_POOL_IO_POLICY = new ConfigKey<>(String.class,
+            "kvm.storage.pool.io.policy",
+            "Storage",
+            null,
+            "IO driver policy - 'threads', 'native' or 'io_uring'. If the IO policy is set for a specific storage and enabled in the VM settings this option will override be overridden from the VM's setting",
+            false,
+            ConfigKey.Scope.StoragePool,
+            null);
+
     ConfigKey<Integer> PRIMARY_STORAGE_DOWNLOAD_WAIT = new ConfigKey<Integer>("Storage", Integer.class, "primary.storage.download.wait", "10800",
             "In second, timeout for download template to primary storage", false);
 
diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
index 0b1c1bc0c33..4808733bd2e 100644
--- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
+++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
@@ -41,6 +41,7 @@ import com.cloud.event.ActionEvent;
 import com.cloud.storage.StorageUtil;
 
 import org.apache.cloudstack.api.ApiCommandResourceType;
+import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.secret.dao.PassphraseDao;
 import org.apache.cloudstack.secret.PassphraseVO;
@@ -1520,6 +1521,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
             }
         }
 
+        setIoDriverPolicy(details, storagePool, volume);
         ChapInfo chapInfo = volService.getChapInfo(volumeInfo, dataStore);
 
         if (chapInfo != null) {
@@ -1532,6 +1534,23 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
         return details;
     }
 
+    private void setIoDriverPolicy(Map<String, String> details, StoragePoolVO storagePool, VolumeVO volume) {
+        if (volume.getInstanceId() != null) {
+            UserVmDetailVO ioDriverPolicy = userVmDetailsDao.findDetail(volume.getInstanceId(),
+                    VmDetailConstants.IO_POLICY);
+            if (ioDriverPolicy != null) {
+                if (IoDriverPolicy.STORAGE_SPECIFIC.toString().equals(ioDriverPolicy.getValue())) {
+                    String storageIoPolicyDriver = StorageManager.STORAGE_POOL_IO_POLICY.valueIn(storagePool.getId());
+                    if (storageIoPolicyDriver != null) {
+                        details.put(VmDetailConstants.IO_POLICY, storageIoPolicyDriver);
+                    }
+                } else {
+                    details.put(VmDetailConstants.IO_POLICY, ioDriverPolicy.getValue());
+                }
+            }
+        }
+    }
+
     private static enum VolumeTaskType {
         RECREATE, NOP, MIGRATE
     }
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 414e0e870d6..11f19142833 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
@@ -46,6 +46,7 @@ import javax.naming.ConfigurationException;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.ParserConfigurationException;
 
+import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy;
 import org.apache.cloudstack.storage.configdrive.ConfigDrive;
 import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
 import org.apache.cloudstack.storage.to.TemplateObjectTO;
@@ -1063,8 +1064,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         }
 
         // Enable/disable IO driver for Qemu (in case it is not set CloudStack can also detect if its supported by qemu)
-        Boolean enableIoUringConfig = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.ENABLE_IO_URING);
-        enableIoUring = isIoUringEnabled(enableIoUringConfig);
+        enableIoUring = isIoUringEnabled();
         s_logger.info("IO uring driver for Qemu: " + (enableIoUring ? "enabled" : "disabled"));
 
         final String cpuArchOverride = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.GUEST_CPU_ARCH);
@@ -2507,7 +2507,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         }
 
         if (busT == DiskDef.DiskBus.SCSI) {
-            devices.addDevice(createSCSIDef(vcpus));
+            Map<String, String> details = vmTO.getDetails();
+
+            boolean isIothreadsEnabled = details != null && details.containsKey(VmDetailConstants.IOTHREADS);
+            devices.addDevice(createSCSIDef(vcpus, isIothreadsEnabled));
         }
         return devices;
     }
@@ -2545,8 +2548,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
      * Creates Virtio SCSI controller. <br>
      * The respective Virtio SCSI XML definition is generated only if the VM's Disk Bus is of ISCSI.
      */
-    protected SCSIDef createSCSIDef(int vcpus) {
-        return new SCSIDef((short)0, 0, 0, 9, 0, vcpus);
+    protected SCSIDef createSCSIDef(int vcpus, boolean isIothreadsEnabled) {
+        return new SCSIDef((short)0, 0, 0, 9, 0, vcpus, isIothreadsEnabled);
     }
 
     protected ConsoleDef createConsoleDef() {
@@ -2673,13 +2676,16 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         guest.setGuestArch(_guestCpuArch != null ? _guestCpuArch : vmTO.getArch());
         guest.setMachineType(isGuestAarch64() ? VIRT : PC);
         guest.setBootType(GuestDef.BootType.BIOS);
-        if (MapUtils.isNotEmpty(customParams) && customParams.containsKey(GuestDef.BootType.UEFI.toString())) {
-            guest.setBootType(GuestDef.BootType.UEFI);
-            guest.setBootMode(GuestDef.BootMode.LEGACY);
-            guest.setMachineType(Q35);
-            if (SECURE.equalsIgnoreCase(customParams.get(GuestDef.BootType.UEFI.toString()))) {
-                guest.setBootMode(GuestDef.BootMode.SECURE);
+        if (MapUtils.isNotEmpty(customParams)) {
+            if (customParams.containsKey(GuestDef.BootType.UEFI.toString())) {
+                guest.setBootType(GuestDef.BootType.UEFI);
+                guest.setBootMode(GuestDef.BootMode.LEGACY);
+                guest.setMachineType(Q35);
+                if (SECURE.equalsIgnoreCase(customParams.get(GuestDef.BootType.UEFI.toString()))) {
+                    guest.setBootMode(GuestDef.BootMode.SECURE);
+                }
             }
+            guest.setIothreads(customParams.containsKey(VmDetailConstants.IOTHREADS));
         }
         guest.setUuid(uuid);
         guest.setBootOrder(GuestDef.BootOrder.CDROM);
@@ -2823,6 +2829,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         if (isUefiEnabled) {
             isSecureBoot = isSecureMode(details.get(GuestDef.BootType.UEFI.toString()));
         }
+
         if (vmSpec.getOs().toLowerCase().contains("window")) {
             isWindowsTemplate =true;
         }
@@ -2913,7 +2920,18 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
                     disk.setDiscard(DiscardType.UNMAP);
                 }
 
-                setDiskIoDriver(disk);
+                boolean iothreadsEnabled = MapUtils.isNotEmpty(details) && details.containsKey(VmDetailConstants.IOTHREADS);
+                disk.isIothreadsEnabled(iothreadsEnabled);
+
+                String ioDriver =  null;
+
+                if (MapUtils.isNotEmpty(volume.getDetails()) && volume.getDetails().containsKey(VmDetailConstants.IO_POLICY)) {
+                    ioDriver = volume.getDetails().get(VmDetailConstants.IO_POLICY).toUpperCase();
+                } else if (iothreadsEnabled) {
+                    ioDriver = IoDriverPolicy.THREADS.name();
+                }
+
+                setDiskIoDriver(disk, getIoDriverForTheStorage(ioDriver));
 
                 if (pool.getType() == StoragePoolType.RBD) {
                     /*
@@ -3055,24 +3073,34 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
      * (i) Qemu >= 5.0;
      * (ii) Libvirt >= 6.3.0
      */
-    protected void setDiskIoDriver(DiskDef disk) {
-        if (enableIoUring) {
-            disk.setIoDriver(DiskDef.IoDriver.IOURING);
+    public void setDiskIoDriver(DiskDef disk, IoDriverPolicy ioDriver) {
+        s_logger.debug(String.format("Disk IO driver policy [%s]. The host supports the io_uring policy [%s]", ioDriver, enableIoUring));
+        if (ioDriver != null) {
+            if (IoDriverPolicy.IO_URING != ioDriver) {
+                disk.setIoDriver(ioDriver);
+            } else if (enableIoUring) {
+                disk.setIoDriver(IoDriverPolicy.IO_URING);
+            }
+        }
+    }
+
+    public IoDriverPolicy getIoDriverForTheStorage(String ioDriver) {
+        if (ioDriver == null) {
+            return null;
         }
+        return IoDriverPolicy.valueOf(ioDriver);
     }
 
     /**
      * IO_URING supported if the property 'enable.io.uring' is set to true OR it is supported by qemu
      */
-    private boolean isIoUringEnabled(Boolean enableIoUringConfig) {
+    private boolean isIoUringEnabled() {
         boolean meetRequirements = getHypervisorLibvirtVersion() >= HYPERVISOR_LIBVIRT_VERSION_SUPPORTS_IO_URING
                 && getHypervisorQemuVersion() >= HYPERVISOR_QEMU_VERSION_SUPPORTS_IO_URING;
         if (!meetRequirements) {
             return false;
         }
-        return enableIoUringConfig != null ?
-                enableIoUringConfig:
-                (isUbuntuHost() || isIoUringSupportedByQemu());
+        return isUbuntuHost() || isIoUringSupportedByQemu();
     }
 
     public boolean isUbuntuHost() {
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 24e576278dd..b739c0ee0ab 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
@@ -22,6 +22,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy;
 import org.apache.cloudstack.utils.qemu.QemuObject;
 import org.apache.commons.lang.StringEscapeUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -41,6 +42,7 @@ public class LibvirtVMDef {
     private String _desc;
     private String _platformEmulator;
     private final Map<String, Object> components = new HashMap<String, Object>();
+    private static final int NUMBER_OF_IOTHREADS = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.IOTHREADS);
 
     public static class GuestDef {
         enum GuestType {
@@ -105,6 +107,7 @@ public class LibvirtVMDef {
         private String _machine;
         private String _nvram;
         private String _nvramTemplate;
+        private boolean iothreads;
 
         public static final String GUEST_LOADER_SECURE = "guest.loader.secure";
         public static final String GUEST_LOADER_LEGACY = "guest.loader.legacy";
@@ -167,6 +170,10 @@ public class LibvirtVMDef {
             this._bootmode = bootmode;
         }
 
+        public void setIothreads(boolean iothreads) {
+            this.iothreads = iothreads;
+        }
+
         @Override
         public String toString() {
             if (_type == GuestType.KVM) {
@@ -219,6 +226,9 @@ public class LibvirtVMDef {
                     guestDef.append("<smbios mode='sysinfo'/>\n");
                 }
                 guestDef.append("</os>\n");
+                if (iothreads) {
+                    guestDef.append(String.format("<iothreads>%s</iothreads>", NUMBER_OF_IOTHREADS));
+                }
                 return guestDef.toString();
             } else if (_type == GuestType.LXC) {
                 StringBuilder guestDef = new StringBuilder();
@@ -571,6 +581,7 @@ public class LibvirtVMDef {
     }
 
     public static class DiskDef {
+
         public static class LibvirtDiskEncryptDetails {
             String passphraseUuid;
             QemuObject.EncryptFormat encryptFormat;
@@ -688,26 +699,6 @@ public class LibvirtVMDef {
 
         }
 
-        /**
-         * This enum specifies IO Drivers, each option controls specific policies on I/O.
-         * Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0).
-         */
-        public enum IoDriver {
-            NATIVE("native"),
-            THREADS("threads"),
-            IOURING("io_uring");
-            String ioDriver;
-
-            IoDriver(String driver) {
-                ioDriver = driver;
-            }
-
-            @Override
-            public String toString() {
-                return ioDriver;
-            }
-        }
-
         private DeviceType _deviceType; /* floppy, disk, cdrom */
         private DiskType _diskType;
         private DiskProtocol _diskProtocol;
@@ -738,8 +729,9 @@ public class LibvirtVMDef {
         private String _serial;
         private boolean qemuDriver = true;
         private DiscardType _discard = DiscardType.IGNORE;
-        private IoDriver ioDriver;
+        private IoDriverPolicy ioDriver;
         private LibvirtDiskEncryptDetails encryptDetails;
+        private boolean isIothreadsEnabled;
 
         public DiscardType getDiscard() {
             return _discard;
@@ -749,11 +741,11 @@ public class LibvirtVMDef {
             this._discard = discard;
         }
 
-        public DiskDef.IoDriver getIoDriver() {
+        public IoDriverPolicy getIoDriver() {
             return ioDriver;
         }
 
-        public void setIoDriver(IoDriver ioDriver) {
+        public void setIoDriver(IoDriverPolicy ioDriver) {
             this.ioDriver = ioDriver;
         }
 
@@ -761,6 +753,10 @@ public class LibvirtVMDef {
             _deviceType = deviceType;
         }
 
+        public void isIothreadsEnabled(boolean isIothreadsEnabled) {
+            this.isIothreadsEnabled = isIothreadsEnabled;
+        }
+
         public void defFileBasedDisk(String filePath, String diskLabel, DiskBus bus, DiskFmtType diskFmtType) {
             _diskType = DiskType.FILE;
             _deviceType = DeviceType.DISK;
@@ -1097,9 +1093,12 @@ public class LibvirtVMDef {
                 }
 
                 if(ioDriver != null) {
-                    diskBuilder.append(String.format("io='%s'", ioDriver));
+                    diskBuilder.append(String.format("io='%s' ", ioDriver));
                 }
 
+                if (isIothreadsEnabled && _bus == DiskBus.VIRTIO) {
+                    diskBuilder.append(String.format("iothread='%s' ", NUMBER_OF_IOTHREADS));
+                }
                 diskBuilder.append("/>\n");
             }
 
@@ -1907,6 +1906,7 @@ public class LibvirtVMDef {
         private int slot = 9;
         private int function = 0;
         private int queues = 0;
+        private boolean isIoThreadsEnabled;
 
         public SCSIDef(short index, int domain, int bus, int slot, int function, int queues) {
             this.index = index;
@@ -1917,6 +1917,16 @@ public class LibvirtVMDef {
             this.queues = queues;
         }
 
+        public SCSIDef(short index, int domain, int bus, int slot, int function, int queues, boolean ioThreadsEnabled) {
+            this.index = index;
+            this.domain = domain;
+            this.bus = bus;
+            this.slot = slot;
+            this.function = function;
+            this.queues = queues;
+            this.isIoThreadsEnabled = ioThreadsEnabled;
+        }
+
         public SCSIDef() {
 
         }
@@ -1928,9 +1938,17 @@ public class LibvirtVMDef {
             scsiBuilder.append(String.format("<controller type='scsi' index='%d' model='virtio-scsi'>\n", this.index));
             scsiBuilder.append(String.format("<address type='pci' domain='0x%04X' bus='0x%02X' slot='0x%02X' function='0x%01X'/>\n",
                     this.domain, this.bus, this.slot, this.function ) );
-            if (this.queues > 0) {
-                scsiBuilder.append(String.format("<driver queues='%d'/>\n", this.queues));
+            if (this.queues > 0 || isIoThreadsEnabled) {
+                scsiBuilder.append("<driver");
+                if (queues > 0) {
+                    scsiBuilder.append(String.format(" queues='%s'", queues));
+                }
+                if (isIoThreadsEnabled) {
+                    scsiBuilder.append(String.format(" iothread='%s'", NUMBER_OF_IOTHREADS));
+                }
+                scsiBuilder.append("/>\n");
             }
+
             scsiBuilder.append("</controller>\n");
             return scsiBuilder.toString();
         }
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
index 6a693a95a17..cae872e287f 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
@@ -130,6 +130,8 @@ import com.cloud.utils.UriUtils;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.script.Script;
 import com.cloud.utils.storage.S3.S3Utils;
+import com.cloud.vm.VmDetailConstants;
+
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.HashSet;
@@ -1284,11 +1286,11 @@ public class KVMStorageProcessor implements StorageProcessor {
                                                    final String serial, final Long bytesReadRate, final Long bytesReadRateMax, final Long bytesReadRateMaxLength,
                                                    final Long bytesWriteRate, final Long bytesWriteRateMax, final Long bytesWriteRateMaxLength, final Long iopsReadRate,
                                                    final Long iopsReadRateMax, final Long iopsReadRateMaxLength, final Long iopsWriteRate, final Long iopsWriteRateMax,
-                                                   final Long iopsWriteRateMaxLength, final String cacheMode, final DiskDef.LibvirtDiskEncryptDetails encryptDetails)
+                                                   final Long iopsWriteRateMaxLength, final String cacheMode, final DiskDef.LibvirtDiskEncryptDetails encryptDetails, Map<String, String> details)
             throws LibvirtException, InternalErrorException {
         attachOrDetachDisk(conn, attach, vmName, attachingDisk, devId, serial, bytesReadRate, bytesReadRateMax, bytesReadRateMaxLength,
                 bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength, iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, iopsWriteRate,
-                iopsWriteRateMax, iopsWriteRateMaxLength, cacheMode, encryptDetails, 0l);
+                iopsWriteRateMax, iopsWriteRateMaxLength, cacheMode, encryptDetails, 0l, details);
     }
 
     /**
@@ -1323,8 +1325,9 @@ public class KVMStorageProcessor implements StorageProcessor {
                                                    final Long bytesWriteRate, final Long bytesWriteRateMax, final Long bytesWriteRateMaxLength, final Long iopsReadRate,
                                                    final Long iopsReadRateMax, final Long iopsReadRateMaxLength, final Long iopsWriteRate, final Long iopsWriteRateMax,
                                                    final Long iopsWriteRateMaxLength, final String cacheMode, final DiskDef.LibvirtDiskEncryptDetails encryptDetails,
-                                                   long waitDetachDevice)
+                                                   long waitDetachDevice, Map<String, String> details)
             throws LibvirtException, InternalErrorException {
+
         List<DiskDef> disks = null;
         Domain dm = null;
         DiskDef diskdef = null;
@@ -1448,6 +1451,13 @@ public class KVMStorageProcessor implements StorageProcessor {
                 if(cacheMode != null) {
                     diskdef.setCacheMode(DiskDef.DiskCacheMode.valueOf(cacheMode.toUpperCase()));
                 }
+
+                diskdef.isIothreadsEnabled(details != null && details.containsKey(VmDetailConstants.IOTHREADS));
+
+                String ioDriver = (details != null && details.containsKey(VmDetailConstants.IO_POLICY)) ? details.get(VmDetailConstants.IO_POLICY) : null;
+                if (ioDriver != null) {
+                    resource.setDiskIoDriver(diskdef, resource.getIoDriverForTheStorage(ioDriver.toUpperCase()));
+                }
             }
 
             attachOrDetachDevice(conn, attach, vmName, diskdef, waitDetachDevice);
@@ -1485,7 +1495,7 @@ public class KVMStorageProcessor implements StorageProcessor {
                     vol.getBytesReadRate(), vol.getBytesReadRateMax(), vol.getBytesReadRateMaxLength(),
                     vol.getBytesWriteRate(), vol.getBytesWriteRateMax(), vol.getBytesWriteRateMaxLength(),
                     vol.getIopsReadRate(), vol.getIopsReadRateMax(), vol.getIopsReadRateMaxLength(),
-                    vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, encryptDetails);
+                    vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, encryptDetails, disk.getDetails());
 
             return new AttachAnswer(disk);
         } catch (final LibvirtException e) {
@@ -1521,7 +1531,7 @@ public class KVMStorageProcessor implements StorageProcessor {
                     vol.getBytesReadRate(), vol.getBytesReadRateMax(), vol.getBytesReadRateMaxLength(),
                     vol.getBytesWriteRate(), vol.getBytesWriteRateMax(), vol.getBytesWriteRateMaxLength(),
                     vol.getIopsReadRate(), vol.getIopsReadRateMax(), vol.getIopsReadRateMaxLength(),
-                    vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, null, waitDetachDevice);
+                    vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, null, waitDetachDevice, null);
 
             storagePoolMgr.disconnectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), vol.getPath());
 
diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
index 7bb8b453fc0..ffe20b4ad30 100644
--- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
+++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
@@ -59,6 +59,8 @@ import javax.xml.xpath.XPathExpressionException;
 import javax.xml.xpath.XPathFactory;
 
 import com.cloud.utils.net.NetUtils;
+
+import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy;
 import org.apache.cloudstack.storage.command.AttachAnswer;
 import org.apache.cloudstack.storage.command.AttachCommand;
 import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
@@ -644,7 +646,7 @@ public class LibvirtComputingResourceTest {
     public void testCreateSCSIDef() {
         VirtualMachineTO to = createDefaultVM(false);
 
-        SCSIDef scsiDef = libvirtComputingResourceSpy.createSCSIDef(to.getCpus());
+        SCSIDef scsiDef = libvirtComputingResourceSpy.createSCSIDef(to.getCpus(), false);
         Document domainDoc = parse(scsiDef.toString());
         verifyScsi(to, domainDoc, "");
     }
@@ -5843,25 +5845,25 @@ public class LibvirtComputingResourceTest {
 
     public void setDiskIoDriverTestIoUring() {
         DiskDef diskDef = configureAndTestSetDiskIoDriverTest(HYPERVISOR_LIBVIRT_VERSION_SUPPORTS_IOURING, HYPERVISOR_QEMU_VERSION_SUPPORTS_IOURING);
-        Assert.assertEquals(DiskDef.IoDriver.IOURING, diskDef.getIoDriver());
+        Assert.assertEquals(IoDriverPolicy.IO_URING, diskDef.getIoDriver());
     }
 
     @Test
     public void setDiskIoDriverTestLibvirtSupportsIoUring() {
         DiskDef diskDef = configureAndTestSetDiskIoDriverTest(123l, HYPERVISOR_QEMU_VERSION_SUPPORTS_IOURING);
-        Assert.assertNotEquals(DiskDef.IoDriver.IOURING, diskDef.getIoDriver());
+        Assert.assertNotEquals(IoDriverPolicy.IO_URING, diskDef.getIoDriver());
     }
 
     @Test
     public void setDiskIoDriverTestQemuSupportsIoUring() {
         DiskDef diskDef = configureAndTestSetDiskIoDriverTest(HYPERVISOR_LIBVIRT_VERSION_SUPPORTS_IOURING, 123l);
-        Assert.assertNotEquals(DiskDef.IoDriver.IOURING, diskDef.getIoDriver());
+        Assert.assertNotEquals(IoDriverPolicy.IO_URING, diskDef.getIoDriver());
     }
 
     @Test
     public void setDiskIoDriverTestNoSupportToIoUring() {
         DiskDef diskDef = configureAndTestSetDiskIoDriverTest(123l, 123l);
-        Assert.assertNotEquals(DiskDef.IoDriver.IOURING, diskDef.getIoDriver());
+        Assert.assertNotEquals(IoDriverPolicy.IO_URING, diskDef.getIoDriver());
     }
 
     private DiskDef configureAndTestSetDiskIoDriverTest(long hypervisorLibvirtVersion, long hypervisorQemuVersion) {
@@ -5869,7 +5871,7 @@ public class LibvirtComputingResourceTest {
         LibvirtComputingResource libvirtComputingResourceSpy = Mockito.spy(new LibvirtComputingResource());
         Mockito.when(libvirtComputingResourceSpy.getHypervisorLibvirtVersion()).thenReturn(hypervisorLibvirtVersion);
         Mockito.when(libvirtComputingResourceSpy.getHypervisorQemuVersion()).thenReturn(hypervisorQemuVersion);
-        libvirtComputingResourceSpy.setDiskIoDriver(diskDef);
+        libvirtComputingResourceSpy.setDiskIoDriver(diskDef, IoDriverPolicy.IO_URING);
         return diskDef;
     }
 
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 827a41eeb02..20a139f469f 100644
--- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
@@ -4080,6 +4080,8 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
             options.put(VmDetailConstants.ROOT_DISK_CONTROLLER, Arrays.asList("osdefault", "ide", "scsi", "virtio"));
             options.put(VmDetailConstants.VIDEO_HARDWARE, Arrays.asList("cirrus", "vga", "qxl", "virtio"));
             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"));
         }
 
         if (HypervisorType.VMware.equals(hypervisorType)) {
diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
index 0018cc49d11..93a805a9afd 100644
--- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
@@ -3380,6 +3380,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
                 STORAGE_POOL_DISK_WAIT,
                 STORAGE_POOL_CLIENT_TIMEOUT,
                 STORAGE_POOL_CLIENT_MAX_CONNECTIONS,
+                STORAGE_POOL_IO_POLICY,
                 PRIMARY_STORAGE_DOWNLOAD_WAIT,
                 SecStorageMaxMigrateSessions,
                 MaxDataMigrationWaitTime,
diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index 7b32fdee977..e5dbfd1118c 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -35,6 +35,7 @@ import javax.inject.Inject;
 
 import org.apache.cloudstack.api.ApiErrorCode;
 import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy;
 import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
@@ -4057,6 +4058,16 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
                 details.put(DiskTO.PROTOCOL_TYPE, (volumeToAttach.getPoolType() != null) ? volumeToAttach.getPoolType().toString() : null);
                 details.put(StorageManager.STORAGE_POOL_DISK_WAIT.toString(), String.valueOf(StorageManager.STORAGE_POOL_DISK_WAIT.valueIn(volumeToAttachStoragePool.getId())));
 
+                _userVmDao.loadDetails(vm);
+                if (isIothreadsSupported(vm)) {
+                    details.put(VmDetailConstants.IOTHREADS, VmDetailConstants.IOTHREADS);
+                }
+
+                String ioPolicy = getIoPolicy(vm, volumeToAttachStoragePool.getId());
+                if (ioPolicy != null) {
+                    details.put(VmDetailConstants.IO_POLICY, ioPolicy);
+                }
+
                 if (chapInfo != null) {
                     details.put(DiskTO.CHAP_INITIATOR_USERNAME, chapInfo.getInitiatorUsername());
                     details.put(DiskTO.CHAP_INITIATOR_SECRET, chapInfo.getInitiatorSecret());
@@ -4071,7 +4082,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
                     }
                 }
 
-                _userVmDao.loadDetails(vm);
                 Map<String, String> controllerInfo = new HashMap<String, String>();
                 controllerInfo.put(VmDetailConstants.ROOT_DISK_CONTROLLER, vm.getDetail(VmDetailConstants.ROOT_DISK_CONTROLLER));
                 controllerInfo.put(VmDetailConstants.DATA_DISK_CONTROLLER, vm.getDetail(VmDetailConstants.DATA_DISK_CONTROLLER));
@@ -4169,6 +4179,24 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
         return _volsDao.findById(volumeToAttach.getId());
     }
 
+    private boolean isIothreadsSupported(UserVmVO vm) {
+        return vm.getHypervisorType() == HypervisorType.KVM
+                && vm.getDetails() != null
+                && vm.getDetail(VmDetailConstants.IOTHREADS) != null;
+    }
+
+    private String getIoPolicy(UserVmVO vm, long poolId) {
+        String ioPolicy = null;
+        if (vm.getHypervisorType() == HypervisorType.KVM && vm.getDetails() != null && vm.getDetail(VmDetailConstants.IO_POLICY) != null) {
+            ioPolicy = vm.getDetail(VmDetailConstants.IO_POLICY);
+            if (IoDriverPolicy.STORAGE_SPECIFIC.toString().equals(ioPolicy)) {
+                String storageIoPolicyDriver = StorageManager.STORAGE_POOL_IO_POLICY.valueIn(poolId);
+                ioPolicy = storageIoPolicyDriver != null ? storageIoPolicyDriver : null;
+            }
+        }
+        return ioPolicy;
+    }
+
     private void provideVMInfo(DataStore dataStore, long vmId, Long volumeId) {
         DataStoreDriver dataStoreDriver = dataStore != null ? dataStore.getDriver() : null;
 
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index 72fe27a48d5..8815ea65eb0 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -4516,6 +4516,17 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
                         continue;
                     }
 
+                    if (!hypervisorType.equals(HypervisorType.KVM)) {
+                        if (key.equalsIgnoreCase(VmDetailConstants.IOTHREADS)) {
+                            vm.details.remove(VmDetailConstants.IOTHREADS);
+                            continue;
+                        }
+                        if (key.equalsIgnoreCase(VmDetailConstants.IO_POLICY)) {
+                            vm.details.remove(VmDetailConstants.IO_POLICY);
+                            continue;
+                        }
+                    }
+
                     if (key.equalsIgnoreCase(VmDetailConstants.CPU_NUMBER) ||
                             key.equalsIgnoreCase(VmDetailConstants.CPU_SPEED) ||
                             key.equalsIgnoreCase(VmDetailConstants.MEMORY)) {
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 2c00a45733b..1154af55557 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -656,6 +656,10 @@
 "label.duration": "Duration (in sec)",
 "label.dynamicscalingenabled": "Dynamic scaling enabled",
 "label.dynamicscalingenabled.tooltip": "VM can dynamically scale only when dynamic scaling is enabled on template, service offering and global setting.",
+"label.iothreadsenabled" : "IOThreads",
+"label.iothreadsenabled.tooltip" : "Enable iothreads allocation for KVM hypervisor",
+"label.iodriverpolicy" : "IO driver policy",
+"label.iodriverpolicy.tooltip" : "IO driver policy could be native, io_uring or threads. Choosing the IO policy for a VM will override the storage pool option 'kvm.storage.pool.io.policy' if set (only if iothreads is enabled)",
 "label.diskofferingstrictness": "Disk offering strictness",
 "label.disksizestrictness": "Disk size strictness",
 "label.computeonly.offering": "Compute only disk offering",
diff --git a/ui/src/views/compute/DeployVM.vue b/ui/src/views/compute/DeployVM.vue
index 65d75402fe9..0c87502a44e 100644
--- a/ui/src/views/compute/DeployVM.vue
+++ b/ui/src/views/compute/DeployVM.vue
@@ -689,6 +689,30 @@
                         @select-affinity-group-item="($event) => updateAffinityGroups($event)"
                         @handle-search-filter="($event) => handleSearchFilter('affinityGroups', $event)"/>
                     </a-form-item>
+                    <a-form-item name="iothreadsenabled" ref="iothreadsenabled" v-if="vm.templateid && ['KVM'].includes(hypervisor)">
+                      <template #label>
+                        <tooltip-label :title="$t('label.iothreadsenabled')" :tooltip="$t('label.iothreadsenabled.tooltip')"/>
+                      </template>
+                      <a-form-item name="iothreadsenabled" ref="iothreadsenabled">
+                        <a-switch
+                          v-model:checked="form.iothreadsenabled"
+                          :checked="iothreadsenabled"
+                          @change="val => { iothreadsenabled = val }"/>
+                      </a-form-item>
+                    </a-form-item>
+                    <a-form-item name="iodriverpolicy" ref="iodriverpolicy" v-if="vm.templateid && ['KVM'].includes(hypervisor)">
+                      <template #label>
+                        <tooltip-label :title="$t('label.iodriverpolicy')" :tooltip="$t('label.iodriverpolicy.tooltip')"/>
+                      </template>
+                      <a-select
+                        v-model:value="form.iodriverpolicy"
+                        optionFilterProp="label"
+                        :filterOption="filterOption">
+                        <a-select-option v-for="iodriverpolicy in options.ioPolicyTypes" :key="iodriverpolicy.id">
+                          {{ iodriverpolicy.description }}
+                        </a-select-option>
+                      </a-select>
+                    </a-form-item>
                   </div>
                 </template>
               </a-step>
@@ -892,6 +916,7 @@ export default {
         keyboards: [],
         bootTypes: [],
         bootModes: [],
+        ioPolicyTypes: [],
         dynamicScalingVmConfig: false
       },
       rowCount: {},
@@ -1644,8 +1669,9 @@ export default {
       this.fetchBootTypes()
       this.fetchBootModes()
       this.fetchInstaceGroups()
+      this.fetchIoPolicyTypes()
       nextTick().then(() => {
-        ['name', 'keyboard', 'boottype', 'bootmode', 'userdata'].forEach(this.fillValue)
+        ['name', 'keyboard', 'boottype', 'bootmode', 'userdata', 'iothreadsenabled', 'iodriverpolicy'].forEach(this.fillValue)
         this.form.boottype = this.defaultBootType ? this.defaultBootType : this.options.bootTypes && this.options.bootTypes.length > 0 ? this.options.bootTypes[0].id : undefined
         this.form.bootmode = this.defaultBootMode ? this.defaultBootMode : this.options.bootModes && this.options.bootModes.length > 0 ? this.options.bootModes[0].id : undefined
         this.instanceConfig = toRaw(this.form)
@@ -1689,6 +1715,14 @@ export default {
       }
       this.options.bootModes = bootModes
     },
+    fetchIoPolicyTypes () {
+      this.options.ioPolicyTypes = [
+        { id: 'native', description: 'native' },
+        { id: 'threads', description: 'threads' },
+        { id: 'io_uring', description: 'io_uring' },
+        { id: 'storage_specific', description: 'storage_specific' }
+      ]
+    },
     fetchInstaceGroups () {
       this.options.instanceGroups = []
       api('listInstanceGroups', {
@@ -1933,6 +1967,8 @@ export default {
           deployVmData.bootmode = values.bootmode
         }
         deployVmData.dynamicscalingenabled = values.dynamicscalingenabled
+        deployVmData.iothreadsenabled = values.iothreadsenabled
+        deployVmData.iodriverpolicy = values.iodriverpolicy
         if (values.userdata && values.userdata.length > 0) {
           deployVmData.userdata = encodeURIComponent(btoa(sanitizeReverse(values.userdata)))
         }