vmspawn: add --cxl= option and memory hotplug support
Add --cxl=BOOL option to enable CXL (Compute Express Link) support in the virtual machine. CXL is a high-speed interconnect standard that allows CPUs to access memory attached to devices such as accelerators and memory expanders, enabling flexible memory pooling and expansion beyond what is physically installed on the motherboard. When enabled, adds cxl=on to the QEMU machine configuration. Only supported on x86_64 and aarch64 architectures. This is added for testing purposes and for feature parity with mkosi's CXL= setting. Extend --ram= to accept an optional maximum size for memory hotplug, using the syntax --ram=SIZE[:MAXSIZE] (e.g. --ram=2G:8G). When a maximum is specified, the maxmem key is added to the QEMU memory configuration section to enable memory hotplug up to the given limit. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
Daan De Meyer
parent
b98d532a6c
commit
6a9888c4c6
+17
-3
@@ -167,10 +167,12 @@
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--ram=<replaceable>BYTES</replaceable></option></term>
|
||||
<term><option>--ram=<replaceable>BYTES</replaceable>[:<replaceable>MAXBYTES</replaceable>[:<replaceable>SLOTS</replaceable>]]</option></term>
|
||||
|
||||
<listitem><para>The amount of memory to start the virtual machine with.
|
||||
Defaults to 2G.</para>
|
||||
<listitem><para>The amount of memory to start the virtual machine with. Defaults to 2G.
|
||||
If a maximum size is specified after a colon, memory hotplug is enabled with the given
|
||||
upper limit. The number of hotplug slots can optionally be specified after a second colon
|
||||
and defaults to 1.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/>
|
||||
</listitem>
|
||||
@@ -185,6 +187,18 @@
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--cxl=<replaceable>BOOL</replaceable></option></term>
|
||||
|
||||
<listitem><para>Controls whether to enable CXL (Compute Express Link) support in the virtual
|
||||
machine. CXL is a high-speed interconnect standard that allows CPUs to access memory attached to
|
||||
devices such as accelerators and memory expanders, enabling flexible memory pooling and expansion
|
||||
beyond what is physically installed on the motherboard. Only supported on x86_64 and aarch64
|
||||
architectures.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--vsock=<replaceable>BOOL</replaceable></option></term>
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ _systemd_vmspawn() {
|
||||
local -A OPTS=(
|
||||
[STANDALONE]='-h --help --version -q --quiet --no-pager -n --network-tap --network-user-mode --user --system -x --ephemeral'
|
||||
[PATH]='-D --directory -i --image --linux --initrd --extra-drive --forward-journal --efi-nvram-template'
|
||||
[BOOL]='--kvm --vsock --tpm --discard-disk --register --pass-ssh-key'
|
||||
[BOOL]='--kvm --cxl --vsock --tpm --discard-disk --register --pass-ssh-key'
|
||||
[SECURE_BOOT]='--secure-boot'
|
||||
[FIRMWARE]='--firmware'
|
||||
[FIRMWARE_FEATURES]='--firmware-features'
|
||||
|
||||
@@ -33,6 +33,12 @@
|
||||
# define ARCHITECTURE_SUPPORTS_HPET 0
|
||||
#endif
|
||||
|
||||
#if defined(__x86_64__) || defined(__aarch64__)
|
||||
# define ARCHITECTURE_SUPPORTS_CXL 1
|
||||
#else
|
||||
# define ARCHITECTURE_SUPPORTS_CXL 0
|
||||
#endif
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
# define QEMU_MACHINE_TYPE "q35"
|
||||
#elif defined(__arm__) || defined(__aarch64__) || defined(__riscv) || defined(__loongarch64) || defined(__m68k__)
|
||||
|
||||
+78
-3
@@ -130,7 +130,10 @@ static char *arg_slice = NULL;
|
||||
static char **arg_property = NULL;
|
||||
static char *arg_cpus = NULL;
|
||||
static uint64_t arg_ram = UINT64_C(2) * U64_GB;
|
||||
static uint64_t arg_ram_max = 0;
|
||||
static unsigned arg_ram_slots = 0;
|
||||
static int arg_kvm = -1;
|
||||
static bool arg_cxl = false;
|
||||
static int arg_vsock = -1;
|
||||
static unsigned arg_vsock_cid = VMADDR_CID_ANY;
|
||||
static int arg_tpm = -1;
|
||||
@@ -229,8 +232,11 @@ static int help(void) {
|
||||
" scsi-cd; default: virtio-blk)\n"
|
||||
"\n%3$sHost Configuration:%4$s\n"
|
||||
" --cpus=CPUS Configure number of CPUs in guest\n"
|
||||
" --ram=BYTES Configure guest's RAM size\n"
|
||||
" --ram=BYTES[:MAXBYTES[:SLOTS]]\n"
|
||||
" Configure guest's RAM size (and max/slots for\n"
|
||||
" hotplug)\n"
|
||||
" --kvm=BOOL Enable use of KVM\n"
|
||||
" --cxl=BOOL Enable use of CXL\n"
|
||||
" --vsock=BOOL Override autodetection of VSOCK support\n"
|
||||
" --vsock-cid=CID Specify the CID to use for the guest's VSOCK support\n"
|
||||
" --tpm=BOOL Enable use of a virtual TPM\n"
|
||||
@@ -326,6 +332,42 @@ static int parse_environment(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_ram(const char *s) {
|
||||
_cleanup_free_ char *ram = NULL, *ram_max = NULL, *ram_slots = NULL;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
const char *p = s;
|
||||
r = extract_many_words(&p, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &ram, &ram_max, &ram_slots);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --ram=%s: %m", s);
|
||||
if (r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse --ram=%s", s);
|
||||
if (!isempty(p))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected trailing data in --ram=%s", s);
|
||||
|
||||
r = parse_size(ram, 1024, &arg_ram);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --ram=%s: %m", s);
|
||||
|
||||
if (!isempty(ram_max)) {
|
||||
r = parse_size(ram_max, 1024, &arg_ram_max);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --ram=%s: %m", s);
|
||||
} else
|
||||
arg_ram_max = 0;
|
||||
|
||||
if (!isempty(ram_slots)) {
|
||||
r = safe_atou(ram_slots, &arg_ram_slots);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --ram=%s: %m", s);
|
||||
} else
|
||||
arg_ram_slots = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
@@ -340,6 +382,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_CPUS,
|
||||
ARG_RAM,
|
||||
ARG_KVM,
|
||||
ARG_CXL,
|
||||
ARG_VSOCK,
|
||||
ARG_VSOCK_CID,
|
||||
ARG_TPM,
|
||||
@@ -398,6 +441,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "ram", required_argument, NULL, ARG_RAM },
|
||||
{ "qemu-mem", required_argument, NULL, ARG_RAM }, /* Compat alias */
|
||||
{ "kvm", required_argument, NULL, ARG_KVM },
|
||||
{ "cxl", required_argument, NULL, ARG_CXL },
|
||||
{ "qemu-kvm", required_argument, NULL, ARG_KVM }, /* Compat alias */
|
||||
{ "vsock", required_argument, NULL, ARG_VSOCK },
|
||||
{ "qemu-vsock", required_argument, NULL, ARG_VSOCK }, /* Compat alias */
|
||||
@@ -518,9 +562,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case ARG_RAM:
|
||||
r = parse_size(optarg, 1024, &arg_ram);
|
||||
r = parse_ram(optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --ram=%s: %m", optarg);
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_KVM:
|
||||
@@ -529,6 +573,15 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_CXL:
|
||||
r = parse_boolean_argument("--cxl=", optarg, &arg_cxl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (arg_cxl && !ARCHITECTURE_SUPPORTS_CXL)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"CXL not supported on %s.", architecture_to_string(native_architecture()));
|
||||
break;
|
||||
|
||||
case ARG_VSOCK:
|
||||
r = parse_tristate_argument_with_auto("--vsock=", optarg, &arg_vsock);
|
||||
if (r < 0)
|
||||
@@ -993,6 +1046,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (!strv_isempty(arg_bind_user_groups) && strv_isempty(arg_bind_user))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --bind-user-group= without --bind-user=");
|
||||
|
||||
if (arg_ram_max > 0 && arg_ram_max < arg_ram)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Maximum RAM size must be greater than or equal to initial RAM size");
|
||||
|
||||
if (arg_ram_slots > 0 && arg_ram_max == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Memory hotplug slots require a maximum RAM size");
|
||||
|
||||
if (arg_ephemeral && arg_extra_drives.n_drives > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --ephemeral with --extra-drive=");
|
||||
|
||||
@@ -2311,6 +2370,12 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (arg_cxl) {
|
||||
r = qemu_config_key(config_file, "cxl", "on");
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (arg_directory || arg_runtime_mounts.n_mounts != 0) {
|
||||
r = qemu_config_key(config_file, "memory-backend", "mem");
|
||||
if (r < 0)
|
||||
@@ -2333,6 +2398,16 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_ram_max > 0) {
|
||||
r = qemu_config_keyf(config_file, "maxmem", "%" PRIu64 "M", DIV_ROUND_UP(arg_ram_max, U64_MB));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = qemu_config_keyf(config_file, "slots", "%u", arg_ram_slots > 0 ? arg_ram_slots : 1u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = qemu_config_section(config_file, "object", "rng0",
|
||||
"qom-type", "rng-random",
|
||||
"filename", "/dev/urandom");
|
||||
|
||||
Reference in New Issue
Block a user