package org.ovirt.engine.core.common.utils;
import org.ovirt.engine.core.common.businessentities.ArchitectureType;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VmBase;
import org.ovirt.engine.core.common.businessentities.VmTemplate;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.compat.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VmCpuCountHelper {
private static final int maxBitWidth = 8;
private static final Logger log = LoggerFactory.getLogger(VmCpuCountHelper.class);
private static int bitWidth(int n) {
return n == 0 ? 0 : 32 - Integer.numberOfLeadingZeros(n - 1);
}
private static ArchitectureType architectureFamily(VM vm) {
ArchitectureType clusterArchitecture = vm.getClusterArch();
return clusterArchitecture == null ? null : clusterArchitecture.getFamily();
}
private static ArchitectureType architectureFamily(VmTemplate vmTemplate) {
ArchitectureType clusterArchitecture = vmTemplate.getClusterArch();
return clusterArchitecture == null ? null : clusterArchitecture.getFamily();
}
static Integer calcMaxVCpu(ArchitectureType architecture, Integer maxSockets, Integer maxVCpus,
int threadsPerCore, int cpuPerSocket) {
if (architecture == null || architecture == ArchitectureType.x86) {
// As described in https://bugzilla.redhat.com/1406243#c13, the
// maximum number of vCPUs is limited by thread and core numbers on
// x86, to fit into an 8-bit APIC ID value organized by certain
// rules. That limit is going to be removed once x2APIC is used.
int oneSocketBitWidth = bitWidth(threadsPerCore) + bitWidth(cpuPerSocket);
if (oneSocketBitWidth > maxBitWidth) {
log.warn("{} cores with {} threads may be too many for the VM to be able to run",
cpuPerSocket, threadsPerCore);
} else {
int apicIdLimit = (int) Math.pow(2, 8 - oneSocketBitWidth);
int apicVCpusLimit = cpuPerSocket * threadsPerCore * Math.min(maxSockets, apicIdLimit);
if (apicVCpusLimit == 256) {
// Using the maximum 8-bit value may be unsafe under certain circumstances, see
// https://bugzilla.redhat.com/1406243#c17
// Note that maxVCpus must match the socket and thread counts.
apicVCpusLimit -= cpuPerSocket * threadsPerCore;
}
if (maxVCpus < apicVCpusLimit) {
// The value must be multiplication of cpuPerSocket * threadsPerCore.
maxVCpus = (maxVCpus / (cpuPerSocket * threadsPerCore)) * (cpuPerSocket * threadsPerCore);
} else {
maxVCpus = apicVCpusLimit;
}
}
} else {
maxVCpus = cpuPerSocket * threadsPerCore *
Math.min(maxSockets, maxVCpus / (cpuPerSocket * threadsPerCore));
}
return maxVCpus;
}
/**
* Computes the maximum allowed number of vCPUs that can be assigned
* to a VM according to the specified compatibility level.
*
* @param vm The VM for which we want to know the maximum
* @param compatibilityVersion The compatibility level
* @param architecture Architecture family of the VM
* @return The maximum supported number of vCPUs
*/
private static Integer calcMaxVCpu(VmBase vm, Version compatibilityVersion, ArchitectureType architecture) {
Integer maxSockets = Config.<Integer>getValue(
ConfigValues.MaxNumOfVmSockets,
compatibilityVersion.getValue());
Integer maxVCpus = Config.<Integer>getValue(
ConfigValues.MaxNumOfVmCpus,
compatibilityVersion.getValue());
int threadsPerCore = vm.getThreadsPerCpu();
int cpuPerSocket = vm.getCpuPerSocket();
return calcMaxVCpu(architecture, maxSockets, maxVCpus, threadsPerCore, cpuPerSocket);
}
/**
* Computes the maximum allowed number of vCPUs that can be assigned
* to a VM according to the specified compatibility level.
*
* @param vm The VM for which we want to know the maximum
* @param compatibilityVersion The compatibility level
* @return The maximum supported number of vCPUs
*/
public static Integer calcMaxVCpu(VM vm, Version compatibilityVersion) {
return calcMaxVCpu(vm.getStaticData(), compatibilityVersion, architectureFamily(vm));
}
/**
* Computes the maximum allowed number of vCPUs that can be assigned
* to a VM according to the specified compatibility level.
*
* @param vmTemplate The VM template for which we want to know the maximum
* @param compatibilityVersion The compatibility level
* @return The maximum supported number of vCPUs
*/
public static Integer calcMaxVCpu(VmTemplate vmTemplate, Version compatibilityVersion) {
return calcMaxVCpu(vmTemplate, compatibilityVersion, architectureFamily(vmTemplate));
}
/**
* Validates CPU count configuration. It may not be possible to use
* configurations with too many threads or cores.
*
* @param vm The VM for which we want to check the CPU count configuration
* @return Whether the CPU count configuration is valid
*/
public static boolean validateCpuCounts(VM vm) {
ArchitectureType architecture = architectureFamily(vm);
if (architecture == null || architecture == ArchitectureType.x86) {
return bitWidth(vm.getThreadsPerCpu()) + bitWidth(vm.getCpuPerSocket()) <= maxBitWidth;
}
return true;
}
}