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

rohit 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 27124c10319 Add ability to set cpu.threadspercore similar to existing 
cpu.corespersocket (#8850)
27124c10319 is described below

commit 27124c10319db90ab929222e52f7eedfe7f8b4e9
Author: Marcus Sorensen <marcus_soren...@apple.com>
AuthorDate: Wed Apr 24 06:31:21 2024 -0600

    Add ability to set cpu.threadspercore similar to existing 
cpu.corespersocket (#8850)
    
    * Add ability to set cpu.threadspercore similar to existing 
cpu.corespersocket
    
    * add cpu.threadspercore to VM and template detail options
    
    * Update 
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
    
    Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anapa...@gmail.com>
    
    * add vm detail for KVM
    
    ---------
    
    Co-authored-by: Marcus Sorensen <m...@apple.com>
    Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anapa...@gmail.com>
---
 .../main/java/com/cloud/vm/VmDetailConstants.java  |  1 +
 .../kvm/resource/LibvirtComputingResource.java     | 41 +++++++---
 .../kvm/resource/LibvirtDomainXMLParser.java       |  5 +-
 .../hypervisor/kvm/resource/LibvirtVMDef.java      | 12 ++-
 .../kvm/resource/LibvirtCpuTopologyTest.java       | 94 ++++++++++++++++++++++
 .../kvm/resource/LibvirtDomainXMLParserTest.java   |  7 +-
 .../hypervisor/kvm/resource/LibvirtVMDefTest.java  | 13 +++
 .../java/com/cloud/api/query/QueryManagerImpl.java |  1 +
 8 files changed, 155 insertions(+), 19 deletions(-)

diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java 
b/api/src/main/java/com/cloud/vm/VmDetailConstants.java
index 9338cc11cd4..603948d76cf 100644
--- a/api/src/main/java/com/cloud/vm/VmDetailConstants.java
+++ b/api/src/main/java/com/cloud/vm/VmDetailConstants.java
@@ -19,6 +19,7 @@ package com.cloud.vm;
 public interface VmDetailConstants {
     String KEYBOARD = "keyboard";
     String CPU_CORE_PER_SOCKET = "cpu.corespersocket";
+    String CPU_THREAD_PER_CORE = "cpu.threadspercore";
     String ROOT_DISK_SIZE = "rootdisksize";
     String BOOT_MODE = "boot.mode";
     String NAME_ON_HYPERVISOR= "nameonhypervisor";
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 11cf6328666..5eed56806b8 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
@@ -5149,31 +5149,48 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
         return false;
     }
 
-    private void setCpuTopology(CpuModeDef cmd, int vCpusInDef, Map<String, 
String> details) {
+    protected void setCpuTopology(CpuModeDef cmd, int vCpusInDef, Map<String, 
String> details) {
         if (!enableManuallySettingCpuTopologyOnKvmVm) {
             LOGGER.debug(String.format("Skipping manually setting CPU topology 
on VM's XML due to it is disabled in agent.properties {\"property\": \"%s\", 
\"value\": %s}.",
               
AgentProperties.ENABLE_MANUALLY_SETTING_CPU_TOPOLOGY_ON_KVM_VM.getName(), 
enableManuallySettingCpuTopologyOnKvmVm));
             return;
         }
-        // multi cores per socket, for larger core configs
-        int numCoresPerSocket = -1;
+
+        int numCoresPerSocket = 1;
+        int numThreadsPerCore = 1;
+
         if (details != null) {
-            final String coresPerSocket = 
details.get(VmDetailConstants.CPU_CORE_PER_SOCKET);
-            final int intCoresPerSocket = NumbersUtil.parseInt(coresPerSocket, 
numCoresPerSocket);
-            if (intCoresPerSocket > 0 && vCpusInDef % intCoresPerSocket == 0) {
-                numCoresPerSocket = intCoresPerSocket;
-            }
+            numCoresPerSocket = 
NumbersUtil.parseInt(details.get(VmDetailConstants.CPU_CORE_PER_SOCKET), 1);
+            numThreadsPerCore = 
NumbersUtil.parseInt(details.get(VmDetailConstants.CPU_THREAD_PER_CORE), 1);
         }
-        if (numCoresPerSocket <= 0) {
+
+        if ((numCoresPerSocket * numThreadsPerCore) > vCpusInDef) {
+            LOGGER.warn(String.format("cores per socket (%d) * threads per 
core (%d) exceeds total VM cores. Ignoring extra topology", numCoresPerSocket, 
numThreadsPerCore));
+            numCoresPerSocket = 1;
+            numThreadsPerCore = 1;
+        }
+
+        if (vCpusInDef % (numCoresPerSocket * numThreadsPerCore) != 0) {
+            LOGGER.warn(String.format("cores per socket(%d) * threads per 
core(%d) doesn't divide evenly into total VM cores(%d). Ignoring extra 
topology", numCoresPerSocket, numThreadsPerCore, vCpusInDef));
+            numCoresPerSocket = 1;
+            numThreadsPerCore = 1;
+        }
+
+        // Set default coupling (makes 4 or 6 core sockets for larger core 
configs)
+        int numTotalSockets = 1;
+        if (numCoresPerSocket == 1 && numThreadsPerCore == 1) {
             if (vCpusInDef % 6 == 0) {
                 numCoresPerSocket = 6;
             } else if (vCpusInDef % 4 == 0) {
                 numCoresPerSocket = 4;
             }
+            numTotalSockets = vCpusInDef / numCoresPerSocket;
+        } else {
+            int nTotalCores = vCpusInDef / numThreadsPerCore;
+            numTotalSockets = nTotalCores / numCoresPerSocket;
         }
-        if (numCoresPerSocket > 0) {
-            cmd.setTopology(numCoresPerSocket, vCpusInDef / numCoresPerSocket);
-        }
+
+        cmd.setTopology(numCoresPerSocket, numThreadsPerCore, numTotalSockets);
     }
 
     public void setBackingFileFormat(String volPath) {
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 5465e289a2c..46620e77fda 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
@@ -545,8 +545,9 @@ public class LibvirtDomainXMLParser {
             }
             final String sockets = getAttrValue("topology", "sockets", 
cpuModeDefElement);
             final String cores = getAttrValue("topology", "cores", 
cpuModeDefElement);
-            if (StringUtils.isNotBlank(sockets) && 
StringUtils.isNotBlank(cores)) {
-                cpuModeDef.setTopology(Integer.parseInt(cores), 
Integer.parseInt(sockets));
+            final String threads = getAttrValue("topology", "threads", 
cpuModeDefElement);
+            if (StringUtils.isNotBlank(sockets) && 
StringUtils.isNotBlank(cores) && StringUtils.isNotBlank(threads)) {
+                cpuModeDef.setTopology(Integer.parseInt(cores), 
Integer.parseInt(threads), Integer.parseInt(sockets));
             }
         }
     }
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 ee6530f401a..795254ba31e 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
@@ -1754,6 +1754,7 @@ public class LibvirtVMDef {
         private String _model;
         private List<String> _features;
         private int _coresPerSocket = -1;
+        private int _threadsPerCore = -1;
         private int _sockets = -1;
 
         public void setMode(String mode) {
@@ -1770,8 +1771,9 @@ public class LibvirtVMDef {
             _model = model;
         }
 
-        public void setTopology(int coresPerSocket, int sockets) {
+        public void setTopology(int coresPerSocket, int threadsPerCore, int 
sockets) {
             _coresPerSocket = coresPerSocket;
+            _threadsPerCore = threadsPerCore;
             _sockets = sockets;
         }
 
@@ -1800,9 +1802,9 @@ public class LibvirtVMDef {
                 }
             }
 
-            // add topology
-            if (_sockets > 0 && _coresPerSocket > 0) {
-                modeBuilder.append("<topology sockets='" + _sockets + "' 
cores='" + _coresPerSocket + "' threads='1' />");
+            // add topology. Note we require sockets, cores, and threads 
defined
+            if (_sockets > 0 && _coresPerSocket > 0 && _threadsPerCore > 0) {
+                modeBuilder.append("<topology sockets='" + _sockets + "' 
cores='" + _coresPerSocket + "' threads='"  + _threadsPerCore + "' />");
             }
 
             // close cpu def
@@ -1813,6 +1815,8 @@ public class LibvirtVMDef {
         public int getCoresPerSocket() {
             return _coresPerSocket;
         }
+        public int getThreadsPerCore() { return _threadsPerCore; }
+        public int getSockets() { return _sockets; }
     }
 
     public static class SerialDef {
diff --git 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtCpuTopologyTest.java
 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtCpuTopologyTest.java
new file mode 100644
index 00000000000..f5234fc5ae3
--- /dev/null
+++ 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtCpuTopologyTest.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.cloud.hypervisor.kvm.resource;
+
+import com.cloud.vm.VmDetailConstants;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+@RunWith(value = Parameterized.class)
+public class LibvirtCpuTopologyTest {
+    private final LibvirtComputingResource libvirtComputingResource = 
Mockito.spy(new LibvirtComputingResource());
+
+    private final String desc;
+    private final Integer coresPerSocket;
+    private final Integer threadsPerCore;
+    private final Integer totalVmCores;
+    private final String expectedXml;
+
+    public LibvirtCpuTopologyTest(String desc, Integer coresPerSocket, Integer 
threadsPerCore, Integer totalVmCores, String expectedXml) {
+        this.desc = desc;
+        this.coresPerSocket = coresPerSocket;
+        this.threadsPerCore = threadsPerCore;
+        this.totalVmCores = totalVmCores;
+        this.expectedXml = expectedXml;
+    }
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] {
+                createTestData("8 cores, 2 per socket",2, null, 8, 
"<cpu><topology sockets='4' cores='2' threads='1' /></cpu>"),
+                createTestData("8 cores, 4 per socket",4, null, 8, 
"<cpu><topology sockets='2' cores='4' threads='1' /></cpu>"),
+                createTestData("8 cores, nothing specified",null, null, 8, 
"<cpu><topology sockets='2' cores='4' threads='1' /></cpu>"),
+                createTestData("12 cores, nothing specified",null, null, 12, 
"<cpu><topology sockets='2' cores='6' threads='1' /></cpu>"),
+                createTestData("8 cores, 2C per socket, 2TPC",2, 2, 8, 
"<cpu><topology sockets='2' cores='2' threads='2' /></cpu>"),
+                createTestData("8 cores, 1C per socket, 2TPC",1, 2, 8, 
"<cpu><topology sockets='4' cores='1' threads='2' /></cpu>"),
+                createTestData("8 cores, default CPS, 2TPC",null, 2, 8, 
"<cpu><topology sockets='4' cores='1' threads='2' /></cpu>"),
+                createTestData("6 cores, default CPS, 2TPC",null, 2, 6, 
"<cpu><topology sockets='3' cores='1' threads='2' /></cpu>"),
+                createTestData("12 cores, 2CPS, 2TPC",2, 2, 12, 
"<cpu><topology sockets='3' cores='2' threads='2' /></cpu>"),
+                createTestData("6 cores, misconfigured cores, CPS, TPC, use 
default topology",2, 2, 6, "<cpu><topology sockets='1' cores='6' threads='1' 
/></cpu>"),
+                createTestData("odd cores, nothing specified use default 
topology",null, null, 3, "<cpu><topology sockets='3' cores='1' threads='1' 
/></cpu>"),
+                createTestData("odd cores, uneven CPS use default topology",2, 
null, 3, "<cpu><topology sockets='3' cores='1' threads='1' /></cpu>"),
+                createTestData("8 cores, 2 CPS, odd threads use default 
topology", 2, 3, 8, "<cpu><topology sockets='2' cores='4' threads='1' 
/></cpu>"),
+                createTestData("1 core, 2 CPS, odd threads use default 
topology", 2, 1, 1, "<cpu><topology sockets='1' cores='1' threads='1' /></cpu>")
+        });
+    }
+
+    private static Object[] createTestData(String desc, Integer 
coresPerSocket, Integer threadsPerCore, int totalVmCores, String expected) {
+        return new Object[] {desc, coresPerSocket, threadsPerCore, 
totalVmCores, expected};
+    }
+
+    @Test
+    public void topologyTest() {
+        LibvirtVMDef.CpuModeDef cpuModeDef = new LibvirtVMDef.CpuModeDef();
+        Map<String, String> details = new HashMap<>();
+
+        if (coresPerSocket != null) {
+            details.put(VmDetailConstants.CPU_CORE_PER_SOCKET, 
coresPerSocket.toString());
+        }
+
+        if (threadsPerCore != null) {
+            details.put(VmDetailConstants.CPU_THREAD_PER_CORE, 
threadsPerCore.toString());
+        }
+
+        if (coresPerSocket == null && threadsPerCore == null) {
+            details = null;
+        }
+
+        libvirtComputingResource.setCpuTopology(cpuModeDef, totalVmCores, 
details);
+        Assert.assertEquals(desc, expectedXml, cpuModeDef.toString());
+    }
+}
diff --git 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java
 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java
index 3813cb39f3c..921bbf0c9a8 100644
--- 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java
+++ 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java
@@ -31,6 +31,7 @@ import 
com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef;
 
 import junit.framework.TestCase;
 import org.apache.cloudstack.utils.qemu.QemuObject;
+import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.junit.MockitoJUnitRunner;
@@ -267,7 +268,7 @@ public class LibvirtDomainXMLParserTest extends TestCase {
                 "  <uuid>aafaaabc-8657-4efc-9c52-3422d4e04088</uuid>\n" +
                 "  <memory unit='KiB'>2097152</memory>\n" +
                 "  <currentMemory unit='KiB'>2097152</currentMemory>\n" +
-                "  <vcpu placement='static'>2</vcpu>\n" +
+                "  <vcpu placement='static'>8</vcpu>\n" +
                 "  <os>\n" +
                 "    <type arch='x86_64' 
machine='pc-i440fx-rhel7.0.0'>hvm</type>\n" +
                 "    <boot dev='hd'/>\n" +
@@ -278,6 +279,7 @@ public class LibvirtDomainXMLParserTest extends TestCase {
                 "  </features>\n" +
                 "  <cpu mode='host-model' check='partial'>\n" +
                 "    <model fallback='allow'/>\n" +
+                "    <topology sockets='1' cores='4' threads='2' />" +
                 "  </cpu>\n" +
                 "  <clock offset='utc'>\n" +
                 "    <timer name='rtc' tickpolicy='catchup'/>\n" +
@@ -380,5 +382,8 @@ public class LibvirtDomainXMLParserTest extends TestCase {
             System.out.println("Got exception " + e.getMessage());
             throw e;
         }
+        Assert.assertEquals("CPU socket count is parsed", 1, 
libvirtDomainXMLParser.getCpuModeDef().getSockets());
+        Assert.assertEquals("CPU cores count is parsed", 4, 
libvirtDomainXMLParser.getCpuModeDef().getCoresPerSocket());
+        Assert.assertEquals("CPU threads count is parsed", 2, 
libvirtDomainXMLParser.getCpuModeDef().getThreadsPerCore());
     }
 }
diff --git 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java
 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java
index 70bde8f7840..c41d487b63c 100644
--- 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java
+++ 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java
@@ -547,4 +547,17 @@ public class LibvirtVMDefTest extends TestCase {
                 "</controller>\n";
         assertEquals(expected, str);
     }
+
+    @Test
+    public void testTopology() {
+        LibvirtVMDef.CpuModeDef cpuModeDef = new LibvirtVMDef.CpuModeDef();
+        cpuModeDef.setTopology(2, 1, 4);
+        assertEquals("<cpu><topology sockets='4' cores='2' threads='1' 
/></cpu>", cpuModeDef.toString());
+    }
+
+    public void testTopologyNoInfo() {
+        LibvirtVMDef.CpuModeDef cpuModeDef = new LibvirtVMDef.CpuModeDef();
+        cpuModeDef.setTopology(-1, -1, 4);
+        assertEquals("<cpu></cpu>", cpuModeDef.toString());
+    }
 }
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 99af161d12d..a03b35e8576 100644
--- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
@@ -4910,6 +4910,7 @@ public class QueryManagerImpl extends 
MutualExclusiveIdsManagerBase implements Q
         options.put(VmDetailConstants.ROOT_DISK_SIZE, Collections.emptyList());
 
         if (HypervisorType.KVM.equals(hypervisorType)) {
+            options.put(VmDetailConstants.CPU_THREAD_PER_CORE, 
Collections.emptyList());
             options.put(VmDetailConstants.NIC_ADAPTER, Arrays.asList("e1000", 
"virtio", "rtl8139", "vmxnet3", "ne2k_pci"));
             options.put(VmDetailConstants.ROOT_DISK_CONTROLLER, 
Arrays.asList("osdefault", "ide", "scsi", "virtio"));
             options.put(VmDetailConstants.VIDEO_HARDWARE, 
Arrays.asList("cirrus", "vga", "qxl", "virtio"));

Reply via email to