Skip to content

Commit

Permalink
core: Increase TSEG size for large VMs with UEFI
Browse files Browse the repository at this point in the history
Large VMs using UEFI may not start if the TSEG size is not big
enough.  There is no clear rule when the TSEG size must be increased.
We increase it in the following cases:

- If the maximum number of VM vCPUs is at least 128.

- If the maximum memory is at least 16 GB, i.e. the memory taken from
  the guest by changing the TSEG size is negligible.

We increase the TSEG size by wild-guess values based on various
comments from Bugzilla, see the comment in the code for more details.
As the TSEG size is taken from the memory available to the guest, we
do not want to make it unnecessarily large by default.  On the other
hand, it’s better to be on the safe side and allocate a bit more than
perhaps needed rather than causing the VM not to start.

We introduce config values for both the limits mentioned above, in
case the selected values don’t work well enough.

Bug-Url: https://bugzilla.redhat.com/2075486
  • Loading branch information
mz-pdm committed Apr 25, 2022
1 parent b013cc7 commit b4adf09
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ public static Stream<MockConfigDescriptor<?>> mockConfiguration() {
MockConfigDescriptor.of(ConfigValues.MaxNumOfVmCpus, version, createMaxNumberOfVmCpusMap()),
MockConfigDescriptor.of(ConfigValues.MaxNumOfCpusCoefficient, 2),
MockConfigDescriptor.of(ConfigValues.ManyVmCpus, 128),
MockConfigDescriptor.of(ConfigValues.UefiBigVmMemoryGB, 16),
MockConfigDescriptor.of(ConfigValues.MaxNumOfVmSockets, version, 16),
MockConfigDescriptor.of(ConfigValues.VM32BitMaxMemorySizeInMB, version, 20480),
MockConfigDescriptor.of(ConfigValues.VM64BitMaxMemorySizeInMB, version, 4194304),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public static Stream<MockConfigDescriptor<?>> mockConfiguration() {
MockConfigDescriptor.of(ConfigValues.MaxNumOfVmCpus, Version.getLast(), createMaxNumberOfVmCpusMap()),
MockConfigDescriptor.of(ConfigValues.MaxNumOfCpusCoefficient, 2),
MockConfigDescriptor.of(ConfigValues.ManyVmCpus, 128),
MockConfigDescriptor.of(ConfigValues.UefiBigVmMemoryGB, 16),
MockConfigDescriptor.of(ConfigValues.MaxNumOfVmSockets, Version.getLast(), 16),
MockConfigDescriptor.of(ConfigValues.MaxNumOfCpuPerSocket, Version.getLast(), 254),
MockConfigDescriptor.of(ConfigValues.MaxNumOfThreadsPerCpu, Version.getLast(), 8),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public static Stream<MockConfigDescriptor<?>> mockConfiguration() {
createMaxNumberOfVmCpusMap()),
MockConfigDescriptor.of(ConfigValues.MaxNumOfCpusCoefficient, 2),
MockConfigDescriptor.of(ConfigValues.ManyVmCpus, 128),
MockConfigDescriptor.of(ConfigValues.UefiBigVmMemoryGB, 16),
MockConfigDescriptor.of(ConfigValues.MaxNumOfVmSockets,
COMPAT_VERSION_FOR_CPU_SOCKET_TEST,
MAX_NUM_SOCKETS),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ public static Stream<MockConfigDescriptor<?>> mockConfiguration() {
MockConfigDescriptor.of(ConfigValues.MaxNumOfVmSockets, Version.getLast(), 16),
MockConfigDescriptor.of(ConfigValues.MaxNumOfVmCpus, Version.getLast(), createMaxNumberOfVmCpusMap()),
MockConfigDescriptor.of(ConfigValues.MaxNumOfCpusCoefficient, 2),
MockConfigDescriptor.of(ConfigValues.ManyVmCpus, 128)
MockConfigDescriptor.of(ConfigValues.ManyVmCpus, 128),
MockConfigDescriptor.of(ConfigValues.UefiBigVmMemoryGB, 16)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ public enum ConfigValues {
@TypeConverterAttribute(Integer.class)
ManyVmCpus(ClientAccessLevel.User),
@TypeConverterAttribute(Integer.class)
UefiBigVmMemoryGB(ClientAccessLevel.User),
@TypeConverterAttribute(Integer.class)
NumberVmRefreshesBeforeSave,
@TypeConverterAttribute(Integer.class)
NumberVdsRefreshesBeforeTryToStartUnknownVms,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ public static Stream<MockConfigDescriptor<?>> mockConfiguration() {
MockConfigDescriptor.of(ConfigValues.MaxNumOfVmCpus, Version.getLast(), createMaxNumberOfVmCpusMap()),
MockConfigDescriptor.of(ConfigValues.MaxNumOfVmSockets, Version.getLast(), 4),
MockConfigDescriptor.of(ConfigValues.MaxNumOfCpusCoefficient, 2),
MockConfigDescriptor.of(ConfigValues.ManyVmCpus, 128)
MockConfigDescriptor.of(ConfigValues.ManyVmCpus, 128),
MockConfigDescriptor.of(ConfigValues.UefiBigVmMemoryGB, 16)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,8 @@ private void writeFeatures() {
boolean acpiEnabled = vm.getAcpiEnable();
boolean kaslrEnabled = vmInfoBuildUtils.isKASLRDumpEnabled(vm.getVmOsId());
boolean secureBootEnabled = vm.getBiosType() == BiosType.Q35_SECURE_BOOT;
if (!acpiEnabled && !hypervEnabled && !kaslrEnabled && !secureBootEnabled) {
Integer tsegSize = vmInfoBuildUtils.tsegSizeMB(vm, hostDevicesSupplier);
if (!acpiEnabled && !hypervEnabled && !kaslrEnabled && !secureBootEnabled && tsegSize == null) {
return;
}

Expand Down Expand Up @@ -858,9 +859,15 @@ private void writeFeatures() {
writer.writeElement("vmcoreinfo");
}

if (secureBootEnabled) {
if (secureBootEnabled || tsegSize != null) {
writer.writeStartElement("smm");
writer.writeAttributeString("state", "on");
if (tsegSize != null) {
writer.writeStartElement("tseg");
writer.writeAttributeString("unit", "MiB");
writer.writeRaw(tsegSize.toString());
writer.writeEndElement();
}
writer.writeEndElement();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@ public class VmInfoBuildUtils {
Pattern.compile(String.format(BLOCK_DOMAIN_DISK_PATH, ValidationUtils.GUID,
ValidationUtils.GUID, ValidationUtils.GUID));

private static final int DEFAULT_TSEG_SIZE_MB = 16;
private static final int MANY_VCPUS_TSEG_SIZE_INCREASE_MB = 24;
private static final int PER_TB_TSEG_SIZE_INCREASE_MB = 8;

public static final int NVDIMM_LABEL_SIZE = 128 * 1024;

@Inject
Expand Down Expand Up @@ -1677,4 +1681,46 @@ public static boolean isVmWithHighNumberOfX86Vcpus(VM vm) {
return vm.getClusterArch().getFamily() == ArchitectureType.x86
&& maxNumberOfVcpus(vm) >= VmCpuCountHelper.HIGH_NUMBER_OF_X86_VCPUS;
}

/**
* Return a recommended TSEG size in MB or null to use the default value.
*
* Large VMs with UEFI will not start if they don't have TSEG large enough.
* There is no rule what "large" is, it's a VM with "too many" vCPUs or "too large" memory.
* There are just some suggestions:
* - To use 48 MB for VMs with many vCPUs or large memory:
* https://bugzilla.redhat.com/show_bug.cgi?id=2074149#c22
* - The same in a different context:
* https://bugzilla.redhat.com/show_bug.cgi?id=1469338#c30
* - To increase the size by unspecified amount for many vCPUs and by 8 MB of each TB of
* address space (which is max RAM + NVDIMM size in our case):
* https://bugzilla.redhat.com/show_bug.cgi?id=1469338#c7
* - Confirmation that 48 MB may not be enough for really large VMs:
* https://bugzilla.redhat.com/show_bug.cgi?id=2074149#c28
**/
public Integer tsegSizeMB(VM vm, MemoizingSupplier<Map<String, HostDevice>> hostDevicesSupplier) {
// If UEFI is not used, we should be safe and use the default value.
if (vm.getBiosType() == null || !vm.getBiosType().isOvmf()) {
return null;
}
// If the VM is small, we also use the default value, because the TSEG size is taken
// from the memory available to the guest.
final int cpuLimit = Config.getValue(ConfigValues.ManyVmCpus);
final int smallMemory = Config.<Integer> getValue(ConfigValues.UefiBigVmMemoryGB) * 1024;
if (maxNumberOfVcpus(vm) < cpuLimit && vm.getMaxMemorySizeMb() < smallMemory) {
return null;
}
// Otherwise, we start with the default value (16 MB currently) and add 24 MB
// (which implies at least the recommended value of 48 MB together with the default value
// and additional value per memory) for VMs with many vCPUs and 8 MB per each (even partial)
// TB of RAM. This is probably a bit wasteful but it's better than risking the VM won't
// start.
int size = DEFAULT_TSEG_SIZE_MB;
if (maxNumberOfVcpus(vm) >= cpuLimit) {
size += MANY_VCPUS_TSEG_SIZE_INCREASE_MB;
}
final long memorySize = vm.getMaxMemorySizeMb() + getNvdimmTotalSize(vm, hostDevicesSupplier) / (1024 * 1024);
size += (memorySize / (1024 * 1024) + 1) * PER_TB_TSEG_SIZE_INCREASE_MB;
return size;
}
}
1 change: 1 addition & 0 deletions packaging/dbscripts/upgrade/pre_upgrade/0000_config.sql
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ select fn_db_add_config_value_for_versions_up_to('MaxNumOfVmSockets', '16', '4.5
select fn_db_add_config_value_for_versions_up_to('MaxNumOfVmSockets', '10000', '4.7');
select fn_db_add_config_value('MaxNumOfCpusCoefficient', '2', 'general');
select fn_db_add_config_value('ManyVmCpus', '128', 'general');
select fn_db_add_config_value('UefiBigVmMemoryGB', '16', 'general');
select fn_db_add_config_value('MaxRerunVmOnVdsCount','3','general');
select fn_db_add_config_value('MaxStorageVdsDelayCheckSec','5','general');
select fn_db_add_config_value('MaxStorageVdsTimeoutCheckSec','30','general');
Expand Down
2 changes: 2 additions & 0 deletions packaging/etc/engine-config/engine-config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ MaxNumOfCpusCoefficient.description="The factor between max VM CPUs and configur
MaxNumOfCpusCoefficient.type=Integer
ManyVmCpus.description="How many vCPUs are considered to be many in maximum vCPU number calculations"
ManyVmCpus.type=Integer
UefiBigVmMemoryGB.description="How much UEFI VM memory is considered big enough when changing TSEG size"
UefiBigVmMemoryGB.type=Integer
MaxRerunVmOnVdsCount.description="Max Virtual Machine Rerun Attempts on a Host"
MaxRerunVmOnVdsCount.type=Integer
MaxSchedulerWeight.description="Max weight score for a single scheduler weight module"
Expand Down

0 comments on commit b4adf09

Please sign in to comment.