This is required to support custom CPU models, since the "cpu-models.conf" file is not versioned, and can be changed while a VM using a custom model is running. Changing the file in such a state can lead to a different "-cpu" argument on the receiving side.
This patch fixes this by passing the entire "-cpu" option (extracted from /proc/.../cmdline) as a "qm start" parameter. Note that this is only done if the VM to migrate is using a custom model (which we can check just fine, since the <vmid>.conf *is* versioned with pending changes), thus not breaking any live-migration directionality. Signed-off-by: Stefan Reiter <s.rei...@proxmox.com> --- Changes in v10: * Introduce and use PVE::QemuServer::CPUConfig::get_cpu_from_running_vm helper PVE/API2/Qemu.pm | 7 +++++++ PVE/QemuMigrate.pm | 15 +++++++++++++++ PVE/QemuServer.pm | 13 ++++++++++--- PVE/QemuServer/CPUConfig.pm | 14 ++++++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 6fad972..9e453d2 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -2024,6 +2024,11 @@ __PACKAGE__->register_method({ optional => 1, }, machine => get_standard_option('pve-qemu-machine'), + 'force-cpu' => { + description => "Override QEMU's -cpu argument with the given string.", + type => 'string', + optional => 1, + }, targetstorage => { description => "Target storage for the migration. (Can be '1' to use the same storage id as on the source node.)", type => 'string', @@ -2052,6 +2057,7 @@ __PACKAGE__->register_method({ my $timeout = extract_param($param, 'timeout'); my $machine = extract_param($param, 'machine'); + my $force_cpu = extract_param($param, 'force-cpu'); my $get_root_param = sub { my $value = extract_param($param, $_[0]); @@ -2129,6 +2135,7 @@ __PACKAGE__->register_method({ skiplock => $skiplock, forcemachine => $machine, timeout => $timeout, + forcecpu => $force_cpu, }; PVE::QemuServer::vm_start($storecfg, $vmid, $params, $migrate_opts); diff --git a/PVE/QemuMigrate.pm b/PVE/QemuMigrate.pm index e954eca..c12b2b5 100644 --- a/PVE/QemuMigrate.pm +++ b/PVE/QemuMigrate.pm @@ -17,6 +17,7 @@ use PVE::ReplicationState; use PVE::Storage; use PVE::Tools; +use PVE::QemuServer::CPUConfig; use PVE::QemuServer::Drive; use PVE::QemuServer::Helpers qw(min_version); use PVE::QemuServer::Machine; @@ -227,7 +228,17 @@ sub prepare { $self->{forcemachine} = PVE::QemuServer::Machine::qemu_machine_pxe($vmid, $conf); + # To support custom CPU types, we keep QEMU's "-cpu" parameter intact. + # Since the parameter itself contains no reference to a custom model, + # this makes migration independent of changes to "cpu-models.conf". + if ($conf->{cpu}) { + my $cpuconf = PVE::QemuServer::CPUConfig::parse_cpu_conf_basic($conf->{cpu}); + if ($cpuconf && PVE::QemuServer::CPUConfig::is_custom_model($cpuconf->{cputype})) { + $self->{forcecpu} = PVE::QemuServer::CPUConfig::get_cpu_from_running_vm($pid); + } + } } + my $loc_res = PVE::QemuServer::check_local_resources($conf, 1); if (scalar @$loc_res) { if ($self->{running} || !$self->{opts}->{force}) { @@ -664,6 +675,10 @@ sub phase2 { push @$cmd, '--machine', $self->{forcemachine}; } + if ($self->{forcecpu}) { + push @$cmd, '--force-cpu', $self->{forcecpu}; + } + if ($self->{online_local_volumes}) { push @$cmd, '--targetstorage', ($self->{opts}->{targetstorage} // '1'); } diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 510a995..a0f8429 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -2911,7 +2911,7 @@ sub query_understood_cpu_flags { } sub config_to_command { - my ($storecfg, $vmid, $conf, $defaults, $forcemachine) = @_; + my ($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu) = @_; my $cmd = []; my $globalFlags = []; @@ -3317,7 +3317,12 @@ sub config_to_command { push @$rtcFlags, 'base=localtime'; } - push @$cmd, get_cpu_options($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough); + if ($forcecpu) { + push @$cmd, '-cpu', $forcecpu; + } else { + push @$cmd, get_cpu_options($conf, $arch, $kvm, $kvm_off, + $machine_version, $winversion, $gpu_passthrough); + } PVE::QemuServer::Memory::config($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd); @@ -4800,6 +4805,7 @@ sub vm_start { # statefile => 'tcp', 'unix' for migration or path/volid for RAM state # skiplock => 0/1, skip checking for config lock # forcemachine => to force Qemu machine (rollback/migration) +# forcecpu => a QEMU '-cpu' argument string to override get_cpu_options # timeout => in seconds # paused => start VM in paused state (backup) # resume => resume from hibernation @@ -4845,7 +4851,8 @@ sub vm_start_nolock { print "Resuming suspended VM\n"; } - my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine); + my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, + $defaults, $forcemachine, $params->{forcecpu}); my $migration_ip; my $get_migration_ip = sub { diff --git a/PVE/QemuServer/CPUConfig.pm b/PVE/QemuServer/CPUConfig.pm index 8a55739..70b96be 100644 --- a/PVE/QemuServer/CPUConfig.pm +++ b/PVE/QemuServer/CPUConfig.pm @@ -106,6 +106,8 @@ my @supported_cpu_flags = ( my $cpu_flag_supported_re = qr/([+-])(@{[join('|', @supported_cpu_flags)]})/; my $cpu_flag_any_re = qr/([+-])([a-zA-Z0-9\-_\.]+)/; +our $qemu_cmdline_cpu_re = qr/^((?>[+-]?[\w\-_=]+,?)+)$/; + my $cpu_fmt = { cputype => { description => "Emulated CPU type. Can be default or custom name (custom model names must be prefixed with 'custom-').", @@ -413,6 +415,18 @@ sub add_hyperv_enlightenments { } } +sub get_cpu_from_running_vm { + my ($pid) = @_; + + my $cmdline = PVE::QemuServer::Helpers::parse_cmdline($pid); + die "could not read commandline of running machine\n" + if !$cmdline->{cpu}->{value}; + + # sanitize and untaint value + $cmdline->{cpu}->{value} =~ $qemu_cmdline_cpu_re; + return $1; +} + __PACKAGE__->register(); __PACKAGE__->init(); -- 2.26.0 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel