diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties index 5859a980171..ce9c9c11495 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 4d5d560f08e..b7f8c330879 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 8f21cce45a1..e34f39fde34 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 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 bc6b01c9f39..34e154611a6 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 = ""; + + 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