You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by nv...@apache.org on 2022/04/01 15:13:20 UTC

[cloudstack] branch main updated: KVM: Enhance CPU speed detection on hosts (#6175)

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

nvazquez 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 2f2d6cb  KVM: Enhance CPU speed detection on hosts (#6175)
2f2d6cb is described below

commit 2f2d6cbe38975edce02fe04ee4eb4d25b45aeb16
Author: Nicolas Vazquez <ni...@gmail.com>
AuthorDate: Fri Apr 1 12:12:34 2022 -0300

    KVM: Enhance CPU speed detection on hosts (#6175)
    
    * KVM: Enhance CPU speed detection on hosts
    
    * Update agent/conf/agent.properties
    
    Co-authored-by: dahn <da...@gmail.com>
    
    Co-authored-by: dahn <da...@gmail.com>
---
 agent/conf/agent.properties                        |  3 +++
 .../kvm/resource/LibvirtComputingResource.java     |  6 ++++-
 .../apache/cloudstack/utils/linux/KVMHostInfo.java | 23 +++++++++++-------
 .../cloudstack/utils/linux/KVMHostInfoTest.java    | 28 ++++++++++++++++++++++
 4 files changed, 50 insertions(+), 10 deletions(-)

diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties
index 5859a98..ce9c9c1 100644
--- a/agent/conf/agent.properties
+++ b/agent/conf/agent.properties
@@ -285,3 +285,6 @@ iscsi.session.cleanup.enabled=false
 
 # Enable manually setting CPU's topology on KVM's VM.
 # enable.manually.setting.cpu.topology.on.kvm.vm=true
+
+# Manually set the host CPU MHz, in cases where CPU scaling support detected value is wrong
+# host.cpu.manual.speed.mhz=0
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 4d5d560..b7f8c33 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
@@ -371,6 +371,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
     protected String _rngPath = "/dev/random";
     protected int _rngRatePeriod = 1000;
     protected int _rngRateBytes = 2048;
+    protected int _manualCpuSpeed = 0;
     protected String _agentHooksBasedir = "/etc/cloudstack/agent/hooks";
 
     protected String _agentHooksLibvirtXmlScript = "libvirt-vm-xml-transformer.groovy";
@@ -1034,6 +1035,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
             _noMemBalloon = true;
         }
 
+        value = (String)params.get("host.cpu.manual.speed.mhz");
+        _manualCpuSpeed = NumbersUtil.parseInt(value, 0);
+
         _videoHw = (String) params.get("vm.video.hardware");
         value = (String) params.get("vm.video.ram");
         _videoRam = NumbersUtil.parseInt(value, 0);
@@ -3294,7 +3298,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
     @Override
     public StartupCommand[] initialize() {
 
-        final KVMHostInfo info = new KVMHostInfo(_dom0MinMem, _dom0OvercommitMem);
+        final KVMHostInfo info = new KVMHostInfo(_dom0MinMem, _dom0OvercommitMem, _manualCpuSpeed);
 
         String capabilities = String.join(",", info.getCapabilities());
         if (dpdkSupport) {
diff --git a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
index 8f21cce..e34f39f 100644
--- a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
+++ b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
@@ -44,9 +44,10 @@ public class KVMHostInfo {
     private long overCommitMemory;
     private List<String> capabilities = new ArrayList<>();
 
-    private static String cpuInfoMaxFreqFileName = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
+    private static String cpuInfoFreqFileName = "/sys/devices/system/cpu/cpu0/cpufreq/base_frequency";
 
-    public KVMHostInfo(long reservedMemory, long overCommitMemory) {
+    public KVMHostInfo(long reservedMemory, long overCommitMemory, long manualSpeed) {
+        this.cpuSpeed = manualSpeed;
         this.reservedMemory = reservedMemory;
         this.overCommitMemory = overCommitMemory;
         this.getHostInfoFromLibvirt();
@@ -113,13 +114,13 @@ public class KVMHostInfo {
     }
 
     private static long getCpuSpeedFromFile() {
-        LOGGER.info(String.format("Fetching CPU speed from file [%s].", cpuInfoMaxFreqFileName));
-        try (Reader reader = new FileReader(cpuInfoMaxFreqFileName)) {
-            Long cpuInfoMaxFreq = Long.parseLong(IOUtils.toString(reader).trim());
-            LOGGER.info(String.format("Retrieved value [%s] from file [%s]. This corresponds to a CPU speed of [%s] MHz.", cpuInfoMaxFreq, cpuInfoMaxFreqFileName, cpuInfoMaxFreq / 1000));
-            return cpuInfoMaxFreq / 1000;
+        LOGGER.info(String.format("Fetching CPU speed from file [%s].", cpuInfoFreqFileName));
+        try (Reader reader = new FileReader(cpuInfoFreqFileName)) {
+            Long cpuInfoFreq = Long.parseLong(IOUtils.toString(reader).trim());
+            LOGGER.info(String.format("Retrieved value [%s] from file [%s]. This corresponds to a CPU speed of [%s] MHz.", cpuInfoFreq, cpuInfoFreqFileName, cpuInfoFreq / 1000));
+            return cpuInfoFreq / 1000;
         } catch (IOException | NumberFormatException e) {
-            LOGGER.error(String.format("Unable to retrieve the CPU speed from file [%s]", cpuInfoMaxFreqFileName), e);
+            LOGGER.error(String.format("Unable to retrieve the CPU speed from file [%s]", cpuInfoFreqFileName), e);
             return 0L;
         }
     }
@@ -128,7 +129,11 @@ public class KVMHostInfo {
         try {
             final Connect conn = LibvirtConnection.getConnection();
             final NodeInfo hosts = conn.nodeInfo();
-            this.cpuSpeed = getCpuSpeed(hosts);
+            if (this.cpuSpeed == 0) {
+                this.cpuSpeed = getCpuSpeed(hosts);
+            } else {
+                LOGGER.debug(String.format("Using existing configured CPU frequency %s", this.cpuSpeed));
+            }
 
             /*
              * Some CPUs report a single socket and multiple NUMA cells.
diff --git a/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/linux/KVMHostInfoTest.java b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/linux/KVMHostInfoTest.java
index bc6b01c..34e1546 100644
--- a/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/linux/KVMHostInfoTest.java
+++ b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/linux/KVMHostInfoTest.java
@@ -16,16 +16,26 @@
 // under the License.
 package org.apache.cloudstack.utils.linux;
 
+import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
 import org.apache.commons.lang.SystemUtils;
 
 import org.hamcrest.Matchers;
 import org.junit.Test;
 import org.junit.Assume;
 import org.junit.Assert;
+import org.junit.runner.RunWith;
+import org.libvirt.Connect;
 import org.mockito.Mockito;
 
 import org.libvirt.NodeInfo;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
 
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(value = {LibvirtConnection.class})
+@PowerMockIgnore({"javax.xml.*", "org.w3c.dom.*", "org.apache.xerces.*", "org.xml.*"})
 public class KVMHostInfoTest {
     @Test
     public void getCpuSpeed() {
@@ -34,4 +44,22 @@ public class KVMHostInfoTest {
         nodeInfo.mhz = 1000;
         Assert.assertThat(KVMHostInfo.getCpuSpeed(nodeInfo), Matchers.greaterThan(0l));
     }
+
+    @Test
+    public void manualCpuSpeedTest() throws Exception {
+        PowerMockito.mockStatic(LibvirtConnection.class);
+        Connect conn = Mockito.mock(Connect.class);
+        NodeInfo nodeInfo = Mockito.mock(NodeInfo.class);
+        nodeInfo.mhz = 1000;
+        String capabilitiesXml = "<capabilities></capabilities>";
+
+        PowerMockito.doReturn(conn).when(LibvirtConnection.class, "getConnection");
+        PowerMockito.when(conn.nodeInfo()).thenReturn(nodeInfo);
+        PowerMockito.when(conn.getCapabilities()).thenReturn(capabilitiesXml);
+        PowerMockito.when(conn.close()).thenReturn(0);
+        int manualSpeed = 500;
+
+        KVMHostInfo kvmHostInfo = new KVMHostInfo(10, 10, manualSpeed);
+        Assert.assertEquals(kvmHostInfo.getCpuSpeed(), manualSpeed);
+    }
 }
\ No newline at end of file