KVM/s390x Support: Add support for KVM on s390x architecture (#10038)

Signed-off-by: Niyam Siwach <niyam@ibm.com>
Signed-off-by: Himanshu Mishra <Himanshu.Mishra2@ibm.com>
This commit is contained in:
niyamsw 2025-01-29 22:04:16 +05:30 committed by GitHub
parent 398ffc3b2c
commit 5df15a7aa6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 52 additions and 19 deletions

View File

@ -209,7 +209,7 @@ hypervisor.type=kvm
# the management server would send. # the management server would send.
# In case of arm64 (aarch64), this will change the machine type to 'virt' and # In case of arm64 (aarch64), this will change the machine type to 'virt' and
# adds a SCSI and a USB controller in the domain xml. # adds a SCSI and a USB controller in the domain xml.
# Possible values: x86_64 | aarch64 # Possible values: x86_64 | aarch64 | s390x
# If null (default), defaults to the VM's OS architecture # If null (default), defaults to the VM's OS architecture
#guest.cpu.arch= #guest.cpu.arch=

View File

@ -383,7 +383,7 @@ public class AgentProperties{
/** /**
* This param will set the CPU architecture for the domain to override what the management server would send.<br> * This param will set the CPU architecture for the domain to override what the management server would send.<br>
* In case of arm64 (aarch64), this will change the machine type to 'virt' and add a SCSI and a USB controller in the domain XML.<br> * In case of arm64 (aarch64), this will change the machine type to 'virt' and add a SCSI and a USB controller in the domain XML.<br>
* Possible values: x86_64 | aarch64 <br> * Possible values: x86_64 | aarch64 | s390x <br>
* Data type: String.<br> * Data type: String.<br>
* Default value: <code>null</code> (will set use the architecture of the VM's OS). * Default value: <code>null</code> (will set use the architecture of the VM's OS).
*/ */

View File

@ -18,6 +18,7 @@ package com.cloud.hypervisor.kvm.resource;
import static com.cloud.host.Host.HOST_INSTANCE_CONVERSION; import static com.cloud.host.Host.HOST_INSTANCE_CONVERSION;
import static com.cloud.host.Host.HOST_VOLUME_ENCRYPTION; import static com.cloud.host.Host.HOST_VOLUME_ENCRYPTION;
import static org.apache.cloudstack.utils.linux.KVMHostInfo.isHostS390x;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
@ -244,11 +245,16 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
private static final String LEGACY = "legacy"; private static final String LEGACY = "legacy";
private static final String SECURE = "secure"; private static final String SECURE = "secure";
/**
* Machine type for s390x architecture
*/
private static final String S390X_VIRTIO_DEVICE = "s390-ccw-virtio";
/** /**
* Machine type. * Machine type.
*/ */
private static final String PC = "pc"; private static final String PC = isHostS390x() ? S390X_VIRTIO_DEVICE : "pc";
private static final String VIRT = "virt"; private static final String VIRT = isHostS390x() ? S390X_VIRTIO_DEVICE : "virt";
/** /**
* Possible devices to add to VM. * Possible devices to add to VM.
@ -305,6 +311,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
* Constant that defines ARM64 (aarch64) guest architectures. * Constant that defines ARM64 (aarch64) guest architectures.
*/ */
private static final String AARCH64 = "aarch64"; private static final String AARCH64 = "aarch64";
/**
* Constant that defines IBM Z Arch (s390x) guest architectures.
*/
private static final String S390X = "s390x";
public static final String RESIZE_NOTIFY_ONLY = "NOTIFYONLY"; public static final String RESIZE_NOTIFY_ONLY = "NOTIFYONLY";
public static final String BASEPATH = "/usr/share/cloudstack-common/vms/"; public static final String BASEPATH = "/usr/share/cloudstack-common/vms/";
@ -1796,7 +1806,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
"^dummy", "^dummy",
"^lo", "^lo",
"^p\\d+p\\d+", "^p\\d+p\\d+",
"^vni" "^vni",
"^enc"
}; };
/** /**
@ -2642,12 +2653,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
} }
devices.addDevice(createChannelDef(vmTO)); devices.addDevice(createChannelDef(vmTO));
devices.addDevice(createWatchDogDef()); if (!isGuestS390x()) {
devices.addDevice(createWatchDogDef());
}
devices.addDevice(createVideoDef(vmTO)); devices.addDevice(createVideoDef(vmTO));
devices.addDevice(createConsoleDef()); devices.addDevice(createConsoleDef());
devices.addDevice(createGraphicDef(vmTO)); devices.addDevice(createGraphicDef(vmTO));
devices.addDevice(createTabletInputDef()); if (!isGuestS390x()) {
devices.addDevice(createTabletInputDef());
}
if (isGuestAarch64()) { if (isGuestAarch64()) {
createArm64UsbDef(devices); createArm64UsbDef(devices);
} }
@ -2765,7 +2779,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
FeaturesDef features = new FeaturesDef(); FeaturesDef features = new FeaturesDef();
features.addFeatures(PAE); features.addFeatures(PAE);
features.addFeatures(APIC); features.addFeatures(APIC);
features.addFeatures(ACPI); if (!isHostS390x()) {
features.addFeatures(ACPI);
}
if (isUefiEnabled && isSecureBoot) { if (isUefiEnabled && isSecureBoot) {
features.addFeatures(SMM); features.addFeatures(SMM);
} }
@ -2857,6 +2873,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return AARCH64.equals(guestCpuArch); return AARCH64.equals(guestCpuArch);
} }
private boolean isGuestS390x() {
return S390X.equals(guestCpuArch);
}
/** /**
* Creates a guest definition from a VM specification. * Creates a guest definition from a VM specification.
*/ */
@ -2867,7 +2887,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
guest.setManufacturer(vmTO.getMetadataManufacturer()); guest.setManufacturer(vmTO.getMetadataManufacturer());
guest.setProduct(vmTO.getMetadataProductName()); guest.setProduct(vmTO.getMetadataProductName());
guest.setGuestArch(guestCpuArch != null ? guestCpuArch : vmTO.getArch()); guest.setGuestArch(guestCpuArch != null ? guestCpuArch : vmTO.getArch());
guest.setMachineType(isGuestAarch64() ? VIRT : PC); guest.setMachineType((isGuestAarch64() || isGuestS390x()) ? VIRT : PC);
guest.setBootType(GuestDef.BootType.BIOS); guest.setBootType(GuestDef.BootType.BIOS);
if (MapUtils.isNotEmpty(customParams)) { if (MapUtils.isNotEmpty(customParams)) {
if (customParams.containsKey(GuestDef.BootType.UEFI.toString())) { if (customParams.containsKey(GuestDef.BootType.UEFI.toString())) {
@ -2881,7 +2901,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
guest.setIothreads(customParams.containsKey(VmDetailConstants.IOTHREADS)); guest.setIothreads(customParams.containsKey(VmDetailConstants.IOTHREADS));
} }
guest.setUuid(uuid); guest.setUuid(uuid);
guest.setBootOrder(GuestDef.BootOrder.CDROM); if(!isGuestS390x()) {
guest.setBootOrder(GuestDef.BootOrder.CDROM);
}
guest.setBootOrder(GuestDef.BootOrder.HARDISK); guest.setBootOrder(GuestDef.BootOrder.HARDISK);
return guest; return guest;
} }
@ -3122,7 +3144,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
final DiskDef.DiskType diskType = getDiskType(physicalDisk); final DiskDef.DiskType diskType = getDiskType(physicalDisk);
disk.defISODisk(volPath, devId, isUefiEnabled, diskType); disk.defISODisk(volPath, devId, isUefiEnabled, diskType);
if (guestCpuArch != null && guestCpuArch.equals("aarch64")) { if (guestCpuArch != null && (guestCpuArch.equals("aarch64") || guestCpuArch.equals("s390x"))) {
disk.setBusType(DiskDef.DiskBus.SCSI); disk.setBusType(DiskDef.DiskBus.SCSI);
} }
} else { } else {
@ -3220,7 +3242,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
if (vmSpec.getType() != VirtualMachine.Type.User) { if (vmSpec.getType() != VirtualMachine.Type.User) {
final DiskDef iso = new DiskDef(); final DiskDef iso = new DiskDef();
iso.defISODisk(sysvmISOPath, DiskDef.DiskType.FILE); iso.defISODisk(sysvmISOPath, DiskDef.DiskType.FILE);
if (guestCpuArch != null && guestCpuArch.equals("aarch64")) { if (guestCpuArch != null && (guestCpuArch.equals("aarch64") || guestCpuArch.equals("s390x"))) {
iso.setBusType(DiskDef.DiskBus.SCSI); iso.setBusType(DiskDef.DiskBus.SCSI);
} }
vm.getDevices().addDevice(iso); vm.getDevices().addDevice(iso);
@ -4294,7 +4316,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return DiskDef.DiskBus.VIRTIO; return DiskDef.DiskBus.VIRTIO;
} else if (isUefiEnabled && StringUtils.startsWithAny(platformEmulator, "Windows", "Other")) { } else if (isUefiEnabled && StringUtils.startsWithAny(platformEmulator, "Windows", "Other")) {
return DiskDef.DiskBus.SATA; return DiskDef.DiskBus.SATA;
} else if (guestCpuArch != null && guestCpuArch.equals("aarch64")) { } else if (guestCpuArch != null && (guestCpuArch.equals("aarch64") || guestCpuArch.equals("s390x"))) {
return DiskDef.DiskBus.SCSI; return DiskDef.DiskBus.SCSI;
} else { } else {
return DiskDef.DiskBus.IDE; return DiskDef.DiskBus.IDE;

View File

@ -248,7 +248,9 @@ public class LibvirtVMDef {
guestDef.append("<boot dev='" + bo + "'/>\n"); guestDef.append("<boot dev='" + bo + "'/>\n");
} }
} }
guestDef.append("<smbios mode='sysinfo'/>\n"); if (!(_arch != null && _arch.equals("s390x"))) {
guestDef.append("<smbios mode='sysinfo'/>\n");
}
guestDef.append("</os>\n"); guestDef.append("</os>\n");
if (iothreads) { if (iothreads) {
guestDef.append(String.format("<iothreads>%s</iothreads>", NUMBER_OF_IOTHREADS)); guestDef.append(String.format("<iothreads>%s</iothreads>", NUMBER_OF_IOTHREADS));
@ -580,7 +582,7 @@ public class LibvirtVMDef {
} }
} }
if (_emulator != null && _emulator.endsWith("aarch64")) { if (_emulator != null && (_emulator.endsWith("aarch64") || _emulator.endsWith("s390x"))) {
devicesBuilder.append("<controller type='pci' model='pcie-root'/>\n"); devicesBuilder.append("<controller type='pci' model='pcie-root'/>\n");
for (int i = 0; i < 32; i++) { for (int i = 0; i < 32; i++) {
devicesBuilder.append("<controller type='pci' model='pcie-root-port'/>\n"); devicesBuilder.append("<controller type='pci' model='pcie-root-port'/>\n");
@ -1652,7 +1654,7 @@ public class LibvirtVMDef {
if (_scriptPath != null) { if (_scriptPath != null) {
netBuilder.append("<script path='" + _scriptPath + "'/>\n"); netBuilder.append("<script path='" + _scriptPath + "'/>\n");
} }
if (_pxeDisable) { if (_pxeDisable && !"s390x".equals(System.getProperty("os.arch"))) {
netBuilder.append("<rom bar='off' file=''/>"); netBuilder.append("<rom bar='off' file=''/>");
} }
if (_virtualPortType != null) { if (_virtualPortType != null) {

View File

@ -111,6 +111,10 @@ public class KVMHostInfo {
return cpuArch; return cpuArch;
} }
public static boolean isHostS390x() {
return "s390x".equals(System.getProperty("os.arch"));
}
protected static long getCpuSpeed(final String cpabilities, final NodeInfo nodeInfo) { protected static long getCpuSpeed(final String cpabilities, final NodeInfo nodeInfo) {
long speed = 0L; long speed = 0L;
speed = getCpuSpeedFromCommandLscpu(); speed = getCpuSpeedFromCommandLscpu();
@ -137,6 +141,9 @@ public class KVMHostInfo {
try { try {
LOGGER.info("Fetching CPU speed from command \"lscpu\"."); LOGGER.info("Fetching CPU speed from command \"lscpu\".");
String command = "lscpu | grep -i 'Model name' | head -n 1 | egrep -o '[[:digit:]].[[:digit:]]+GHz' | sed 's/GHz//g'"; String command = "lscpu | grep -i 'Model name' | head -n 1 | egrep -o '[[:digit:]].[[:digit:]]+GHz' | sed 's/GHz//g'";
if(isHostS390x()) {
command = "lscpu | grep 'CPU dynamic MHz' | cut -d ':' -f 2 | tr -d ' ' | awk '{printf \"%.1f\\n\", $1 / 1000}'";
}
String result = Script.runSimpleBashScript(command); String result = Script.runSimpleBashScript(command);
long speed = (long) (Float.parseFloat(result) * 1000); long speed = (long) (Float.parseFloat(result) * 1000);
LOGGER.info(String.format("Command [%s] resulted in the value [%s] for CPU speed.", command, speed)); LOGGER.info(String.format("Command [%s] resulted in the value [%s] for CPU speed.", command, speed));

View File

@ -401,7 +401,7 @@ public class LibvirtComputingResourceTest {
VirtualMachineTO to = createDefaultVM(false); VirtualMachineTO to = createDefaultVM(false);
LibvirtVMDef vm = new LibvirtVMDef(); LibvirtVMDef vm = new LibvirtVMDef();
GuestDef guestDef = libvirtComputingResourceSpy.createGuestFromSpec(to, vm, to.getUuid(), null); GuestDef guestDef = libvirtComputingResourceSpy.createGuestFromSpec(to, vm, to.getUuid(), null);
verifySysInfo(guestDef, "smbios", to.getUuid(), "pc"); verifySysInfo(guestDef, "smbios", to.getUuid(), "s390x".equals(System.getProperty("os.arch")) ? "s390-ccw-virtio" : "pc");
Assert.assertEquals(GuestDef.BootType.BIOS, guestDef.getBootType()); Assert.assertEquals(GuestDef.BootType.BIOS, guestDef.getBootType());
Assert.assertNull(guestDef.getBootMode()); Assert.assertNull(guestDef.getBootMode());
} }
@ -829,7 +829,7 @@ public class LibvirtComputingResourceTest {
} }
private void verifyOsType(Document domainDoc) { private void verifyOsType(Document domainDoc) {
assertXpath(domainDoc, "/domain/os/type/@machine", "pc"); assertXpath(domainDoc, "/domain/os/type/@machine", "s390x".equals(System.getProperty("os.arch")) ? "s390-ccw-virtio" : "pc");
assertXpath(domainDoc, "/domain/os/type/text()", "hvm"); assertXpath(domainDoc, "/domain/os/type/text()", "hvm");
} }

View File

@ -309,6 +309,8 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis
if (vmProfile.getTemplate().getBits() == 32) { if (vmProfile.getTemplate().getBits() == 32) {
to.setArch("i686"); to.setArch("i686");
} else if("s390x".equals(System.getProperty("os.arch"))) {
to.setArch("s390x");
} else { } else {
to.setArch("x86_64"); to.setArch("x86_64");
} }