[pve-devel] [PATCH v7 qemu-server 06/10] Rework get_cpu_options and allow custom CPU models
Stefan Reiter
s.reiter at proxmox.com
Thu Jan 16 16:40:52 CET 2020
If a cputype is custom (check via prefix), try to load options from the
custom CPU model config, and set values accordingly.
While at it, extract currently hardcoded values into seperate sub and add
reasonings.
Since the new flag resolving outputs flags in sorted order for
consistency, adapt the test cases to not break. Only the order is
changed, not which flags are present.
Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
---
PVE/QemuServer/CPUConfig.pm | 191 +++++++++++++-----
test/cfg2cmd/i440fx-win10-hostpci.conf.cmd | 2 +-
test/cfg2cmd/minimal-defaults.conf.cmd | 2 +-
test/cfg2cmd/pinned-version.conf.cmd | 2 +-
.../q35-linux-hostpci-multifunction.conf.cmd | 2 +-
test/cfg2cmd/q35-linux-hostpci.conf.cmd | 2 +-
test/cfg2cmd/q35-win10-hostpci.conf.cmd | 2 +-
test/cfg2cmd/simple1.conf.cmd | 2 +-
test/cfg2cmd/spice-enhancments.conf.cmd | 2 +-
test/cfg2cmd/spice-linux-4.1.conf.cmd | 2 +-
test/cfg2cmd/spice-usb3.conf.cmd | 2 +-
test/cfg2cmd/spice-win.conf.cmd | 2 +-
12 files changed, 152 insertions(+), 61 deletions(-)
diff --git a/PVE/QemuServer/CPUConfig.pm b/PVE/QemuServer/CPUConfig.pm
index 73c9ee5..31dd829 100644
--- a/PVE/QemuServer/CPUConfig.pm
+++ b/PVE/QemuServer/CPUConfig.pm
@@ -361,99 +361,190 @@ sub print_cpuflag_hash {
return $formatted;
}
+sub parse_cpuflag_list {
+ my ($re, $reason, $flaglist) = @_;
+
+ my $res = {};
+ return $res if !$flaglist;
+
+ foreach my $flag (split(";", $flaglist)) {
+ if ($flag =~ $re) {
+ $res->{$2} = { op => $1, reason => $reason };
+ }
+ }
+
+ return $res;
+}
+
# Calculate QEMU's '-cpu' argument from a given VM configuration
sub get_cpu_options {
my ($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough) = @_;
- my $cpuFlags = [];
- my $ostype = $conf->{ostype};
-
- my $cpu = $kvm ? "kvm64" : "qemu64";
+ my $cputype = $kvm ? "kvm64" : "qemu64";
if ($arch eq 'aarch64') {
- $cpu = 'cortex-a57';
+ $cputype = 'cortex-a57';
}
- my $hv_vendor_id;
- if (my $cputype = $conf->{cpu}) {
- my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype)
- or die "Cannot parse cpu description: $cputype\n";
- $cpu = $cpuconf->{cputype};
- $kvm_off = 1 if $cpuconf->{hidden};
- $hv_vendor_id = $cpuconf->{'hv-vendor-id'};
- if (defined(my $flags = $cpuconf->{flags})) {
- push @$cpuFlags, split(";", $flags);
- }
- }
-
- push @$cpuFlags , '+lahf_lm' if $cpu eq 'kvm64' && $arch eq 'x86_64';
+ my $cpu = {};
+ my $custom_cpu;
+ my $hv_vendor_id;
+ if (my $cpu_prop_str = $conf->{cpu}) {
+ $cpu = parse_vm_cpu_conf($cpu_prop_str)
+ or die "Cannot parse cpu description: $cpu_prop_str\n";
- push @$cpuFlags , '-x2apic' if $ostype && $ostype eq 'solaris';
+ $cputype = $cpu->{cputype};
- push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32';
+ if (is_custom_model($cputype)) {
+ $custom_cpu = get_custom_model($cputype);
- push @$cpuFlags, '-rdtscp' if $cpu =~ m/^Opteron/;
+ $cputype = $custom_cpu->{'reported-model'} //
+ $cpu_fmt->{'reported-model'}->{default};
+ $kvm_off = $custom_cpu->{hidden}
+ if defined($custom_cpu->{hidden});
+ $hv_vendor_id = $custom_cpu->{'hv-vendor-id'};
+ }
- if (min_version($machine_version, 2, 3) && $arch eq 'x86_64') {
+ # VM-specific settings override custom CPU config
+ $kvm_off = $cpu->{hidden}
+ if defined($cpu->{hidden});
+ $hv_vendor_id = $cpu->{'hv-vendor-id'}
+ if defined($cpu->{'hv-vendor-id'});
+ }
- push @$cpuFlags , '+kvm_pv_unhalt' if $kvm;
- push @$cpuFlags , '+kvm_pv_eoi' if $kvm;
+ my $pve_flags = get_pve_cpu_flags($conf, $kvm, $cputype, $arch,
+ $machine_version);
+
+ my $hv_flags = get_hyperv_enlightenments($winversion, $machine_version,
+ $conf->{bios}, $gpu_passthrough, $hv_vendor_id) if $kvm;
+
+ my $custom_cputype_flags = parse_cpuflag_list($cpu_flag_any_re,
+ "set by custom CPU model", $custom_cpu->{flags});
+
+ my $vm_flags = parse_cpuflag_list($cpu_flag_supported_re,
+ "manually set for VM", $cpu->{flags});
+
+ my $pve_forced_flags = {};
+ $pve_forced_flags->{'enforce'} = {
+ reason => "error if requested CPU settings not available",
+ } if $cputype ne 'host' && $kvm && $arch eq 'x86_64';
+ $pve_forced_flags->{'kvm'} = {
+ value => "off",
+ reason => "hide KVM virtualization from guest",
+ } if $kvm_off;
+
+ # $cputype is the "reported-model" for custom types, so we can just look up
+ # the vendor in the default list
+ my $cpu_vendor = $cpu_vendor_list->{$cputype};
+ if ($cpu_vendor) {
+ $pve_forced_flags->{'vendor'} = {
+ value => $cpu_vendor,
+ } if $cpu_vendor ne 'default';
+ } elsif ($arch ne 'aarch64') {
+ die "internal error"; # should not happen
}
- add_hyperv_enlightenments($cpuFlags, $winversion, $machine_version, $conf->{bios}, $gpu_passthrough, $hv_vendor_id) if $kvm;
+ my $cpu_str = $cputype;
- push @$cpuFlags, 'enforce' if $cpu ne 'host' && $kvm && $arch eq 'x86_64';
+ # will be resolved in parameter order
+ $cpu_str .= resolve_cpu_flags($pve_flags, $hv_flags, $custom_cputype_flags,
+ $vm_flags, $pve_forced_flags);
- push @$cpuFlags, 'kvm=off' if $kvm_off;
+ return ('-cpu', $cpu_str);
+}
- if (my $cpu_vendor = $cpu_vendor_list->{$cpu}) {
- push @$cpuFlags, "vendor=${cpu_vendor}"
- if $cpu_vendor ne 'default';
- } elsif ($arch ne 'aarch64') {
- die "internal error"; # should not happen
+# Some hardcoded flags required by certain configurations
+sub get_pve_cpu_flags {
+ my ($conf, $kvm, $cputype, $arch, $machine_version) = @_;
+
+ my $pve_flags = {};
+ my $pve_msg = "set by PVE;";
+
+ $pve_flags->{'lahf_lm'} = {
+ op => '+',
+ reason => "$pve_msg to support Windows 8.1+",
+ } if $cputype eq 'kvm64' && $arch eq 'x86_64';
+
+ $pve_flags->{'x2apic'} = {
+ op => '-',
+ reason => "$pve_msg incompatible with Solaris",
+ } if $conf->{ostype} && $conf->{ostype} eq 'solaris';
+
+ $pve_flags->{'sep'} = {
+ op => '+',
+ reason => "$pve_msg to support Windows 8+ and improve Windows XP+",
+ } if $cputype eq 'kvm64' || $cputype eq 'kvm32';
+
+ $pve_flags->{'rdtscp'} = {
+ op => '-',
+ reason => "$pve_msg broken on AMD Opteron",
+ } if $cputype =~ m/^Opteron/;
+
+ if (min_version($machine_version, 2, 3) && $kvm && $arch eq 'x86_64') {
+ $pve_flags->{'kvm_pv_unhalt'} = {
+ op => '+',
+ reason => "$pve_msg to improve Linux guest spinlock performance",
+ };
+ $pve_flags->{'kvm_pv_eoi'} = {
+ op => '+',
+ reason => "$pve_msg to improve Linux guest interrupt performance",
+ };
}
- $cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags);
-
- return ('-cpu', $cpu);
+ return $pve_flags;
}
-sub add_hyperv_enlightenments {
- my ($cpuFlags, $winversion, $machine_version, $bios, $gpu_passthrough, $hv_vendor_id) = @_;
+sub get_hyperv_enlightenments {
+ my ($winversion, $machine_version, $bios, $gpu_passthrough, $hv_vendor_id) = @_;
return if $winversion < 6;
return if $bios && $bios eq 'ovmf' && $winversion < 8;
- if ($gpu_passthrough || defined($hv_vendor_id)) {
+ my $flags = {};
+ my $default_reason = "automatic Hyper-V enlightenment for Windows";
+ my $flagfn = sub {
+ my ($flag, $value, $reason) = @_;
+ $flags->{$flag} = {
+ reason => $reason // $default_reason,
+ value => $value,
+ }
+ };
+
+ my $hv_vendor_set = defined($hv_vendor_id);
+ if ($gpu_passthrough || $hv_vendor_set) {
$hv_vendor_id //= 'proxmox';
- push @$cpuFlags , "hv_vendor_id=$hv_vendor_id";
+ $flagfn->('hv_vendor_id', $hv_vendor_id, $hv_vendor_set ?
+ "custom hv_vendor_id set" : "NVIDIA workaround for GPU passthrough");
}
if (min_version($machine_version, 2, 3)) {
- push @$cpuFlags , 'hv_spinlocks=0x1fff';
- push @$cpuFlags , 'hv_vapic';
- push @$cpuFlags , 'hv_time';
+ $flagfn->('hv_spinlocks', '0x1fff');
+ $flagfn->('hv_vapic');
+ $flagfn->('hv_time');
} else {
- push @$cpuFlags , 'hv_spinlocks=0xffff';
+ $flagfn->('hv_spinlocks', '0xffff');
}
if (min_version($machine_version, 2, 6)) {
- push @$cpuFlags , 'hv_reset';
- push @$cpuFlags , 'hv_vpindex';
- push @$cpuFlags , 'hv_runtime';
+ $flagfn->('hv_reset');
+ $flagfn->('hv_vpindex');
+ $flagfn->('hv_runtime');
}
if ($winversion >= 7) {
- push @$cpuFlags , 'hv_relaxed';
+ my $win7_reason = $default_reason . " 7 and higher";
+ $flagfn->('hv_relaxed', undef, $win7_reason);
if (min_version($machine_version, 2, 12)) {
- push @$cpuFlags , 'hv_synic';
- push @$cpuFlags , 'hv_stimer';
+ $flagfn->('hv_synic', undef, $win7_reason);
+ $flagfn->('hv_stimer', undef, $win7_reason);
}
if (min_version($machine_version, 3, 1)) {
- push @$cpuFlags , 'hv_ipi';
+ $flagfn->('hv_ipi', undef, $win7_reason);
}
}
+
+ return $flags;
}
__PACKAGE__->register();
diff --git a/test/cfg2cmd/i440fx-win10-hostpci.conf.cmd b/test/cfg2cmd/i440fx-win10-hostpci.conf.cmd
index bda7f63..c5a21f4 100644
--- a/test/cfg2cmd/i440fx-win10-hostpci.conf.cmd
+++ b/test/cfg2cmd/i440fx-win10-hostpci.conf.cmd
@@ -15,7 +15,7 @@
-boot 'menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg' \
-vnc unix:/var/run/qemu-server/8006.vnc,password \
-no-hpet \
- -cpu 'kvm64,+lahf_lm,+sep,+kvm_pv_unhalt,+kvm_pv_eoi,hv_spinlocks=0x1fff,hv_vapic,hv_time,hv_reset,hv_vpindex,hv_runtime,hv_relaxed,hv_synic,hv_stimer,hv_ipi,enforce' \
+ -cpu 'kvm64,enforce,hv_ipi,hv_relaxed,hv_reset,hv_runtime,hv_spinlocks=0x1fff,hv_stimer,hv_synic,hv_time,hv_vapic,hv_vpindex,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep' \
-m 512 \
-object 'memory-backend-ram,id=ram-node0,size=256M' \
-numa 'node,nodeid=0,cpus=0,memdev=ram-node0' \
diff --git a/test/cfg2cmd/minimal-defaults.conf.cmd b/test/cfg2cmd/minimal-defaults.conf.cmd
index 83ae328..d42a0c4 100644
--- a/test/cfg2cmd/minimal-defaults.conf.cmd
+++ b/test/cfg2cmd/minimal-defaults.conf.cmd
@@ -12,7 +12,7 @@
-nodefaults \
-boot 'menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg' \
-vnc unix:/var/run/qemu-server/8006.vnc,password \
- -cpu kvm64,+lahf_lm,+sep,+kvm_pv_unhalt,+kvm_pv_eoi,enforce \
+ -cpu kvm64,enforce,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep \
-m 512 \
-device 'pci-bridge,id=pci.1,chassis_nr=1,bus=pci.0,addr=0x1e' \
-device 'pci-bridge,id=pci.2,chassis_nr=2,bus=pci.0,addr=0x1f' \
diff --git a/test/cfg2cmd/pinned-version.conf.cmd b/test/cfg2cmd/pinned-version.conf.cmd
index cc43d22..df55da7 100644
--- a/test/cfg2cmd/pinned-version.conf.cmd
+++ b/test/cfg2cmd/pinned-version.conf.cmd
@@ -12,7 +12,7 @@
-nodefaults \
-boot 'menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg' \
-vnc unix:/var/run/qemu-server/8006.vnc,password \
- -cpu kvm64,+lahf_lm,+sep,+kvm_pv_unhalt,+kvm_pv_eoi,enforce \
+ -cpu kvm64,enforce,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep \
-m 1024 \
-device 'vmgenid,guid=bdd46b98-fefc-11e9-97b4-d72c378e0f96' \
-readconfig /usr/share/qemu-server/pve-q35.cfg \
diff --git a/test/cfg2cmd/q35-linux-hostpci-multifunction.conf.cmd b/test/cfg2cmd/q35-linux-hostpci-multifunction.conf.cmd
index 833f37b..8820b02 100644
--- a/test/cfg2cmd/q35-linux-hostpci-multifunction.conf.cmd
+++ b/test/cfg2cmd/q35-linux-hostpci-multifunction.conf.cmd
@@ -14,7 +14,7 @@
-nodefaults \
-boot 'menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg' \
-vnc unix:/var/run/qemu-server/8006.vnc,password \
- -cpu kvm64,+lahf_lm,+sep,+kvm_pv_unhalt,+kvm_pv_eoi,enforce \
+ -cpu kvm64,enforce,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep \
-m 512 \
-object 'memory-backend-ram,id=ram-node0,size=256M' \
-numa 'node,nodeid=0,cpus=0,memdev=ram-node0' \
diff --git a/test/cfg2cmd/q35-linux-hostpci.conf.cmd b/test/cfg2cmd/q35-linux-hostpci.conf.cmd
index ca5dfac..75b619a 100644
--- a/test/cfg2cmd/q35-linux-hostpci.conf.cmd
+++ b/test/cfg2cmd/q35-linux-hostpci.conf.cmd
@@ -14,7 +14,7 @@
-nodefaults \
-boot 'menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg' \
-vnc unix:/var/run/qemu-server/8006.vnc,password \
- -cpu kvm64,+lahf_lm,+sep,+kvm_pv_unhalt,+kvm_pv_eoi,enforce \
+ -cpu kvm64,enforce,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep \
-m 512 \
-object 'memory-backend-ram,id=ram-node0,size=256M' \
-numa 'node,nodeid=0,cpus=0,memdev=ram-node0' \
diff --git a/test/cfg2cmd/q35-win10-hostpci.conf.cmd b/test/cfg2cmd/q35-win10-hostpci.conf.cmd
index 9531d7d..843ccd2 100644
--- a/test/cfg2cmd/q35-win10-hostpci.conf.cmd
+++ b/test/cfg2cmd/q35-win10-hostpci.conf.cmd
@@ -15,7 +15,7 @@
-boot 'menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg' \
-vnc unix:/var/run/qemu-server/8006.vnc,password \
-no-hpet \
- -cpu 'kvm64,+lahf_lm,+sep,+kvm_pv_unhalt,+kvm_pv_eoi,hv_spinlocks=0x1fff,hv_vapic,hv_time,hv_reset,hv_vpindex,hv_runtime,hv_relaxed,hv_synic,hv_stimer,hv_ipi,enforce' \
+ -cpu 'kvm64,enforce,hv_ipi,hv_relaxed,hv_reset,hv_runtime,hv_spinlocks=0x1fff,hv_stimer,hv_synic,hv_time,hv_vapic,hv_vpindex,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep' \
-m 512 \
-object 'memory-backend-ram,id=ram-node0,size=256M' \
-numa 'node,nodeid=0,cpus=0,memdev=ram-node0' \
diff --git a/test/cfg2cmd/simple1.conf.cmd b/test/cfg2cmd/simple1.conf.cmd
index b5c06cf..3485064 100644
--- a/test/cfg2cmd/simple1.conf.cmd
+++ b/test/cfg2cmd/simple1.conf.cmd
@@ -12,7 +12,7 @@
-nodefaults \
-boot 'menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg' \
-vnc unix:/var/run/qemu-server/8006.vnc,password \
- -cpu kvm64,+lahf_lm,+sep,+kvm_pv_unhalt,+kvm_pv_eoi,enforce \
+ -cpu kvm64,enforce,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep \
-m 768 \
-device 'pci-bridge,id=pci.1,chassis_nr=1,bus=pci.0,addr=0x1e' \
-device 'pci-bridge,id=pci.2,chassis_nr=2,bus=pci.0,addr=0x1f' \
diff --git a/test/cfg2cmd/spice-enhancments.conf.cmd b/test/cfg2cmd/spice-enhancments.conf.cmd
index 2d49b3a..3951c06 100644
--- a/test/cfg2cmd/spice-enhancments.conf.cmd
+++ b/test/cfg2cmd/spice-enhancments.conf.cmd
@@ -12,7 +12,7 @@
-nodefaults \
-boot 'menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg' \
-vnc unix:/var/run/qemu-server/8006.vnc,password \
- -cpu kvm64,+lahf_lm,+sep,+kvm_pv_unhalt,+kvm_pv_eoi,enforce \
+ -cpu kvm64,enforce,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep \
-m 512 \
-device 'pci-bridge,id=pci.1,chassis_nr=1,bus=pci.0,addr=0x1e' \
-device 'pci-bridge,id=pci.2,chassis_nr=2,bus=pci.0,addr=0x1f' \
diff --git a/test/cfg2cmd/spice-linux-4.1.conf.cmd b/test/cfg2cmd/spice-linux-4.1.conf.cmd
index 4ed6fd2..12d8627 100644
--- a/test/cfg2cmd/spice-linux-4.1.conf.cmd
+++ b/test/cfg2cmd/spice-linux-4.1.conf.cmd
@@ -12,7 +12,7 @@
-nodefaults \
-boot 'menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg' \
-vnc unix:/var/run/qemu-server/8006.vnc,password \
- -cpu kvm64,+lahf_lm,+sep,+kvm_pv_unhalt,+kvm_pv_eoi,enforce \
+ -cpu kvm64,enforce,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep \
-m 768 \
-device 'pci-bridge,id=pci.1,chassis_nr=1,bus=pci.0,addr=0x1e' \
-device 'pci-bridge,id=pci.2,chassis_nr=2,bus=pci.0,addr=0x1f' \
diff --git a/test/cfg2cmd/spice-usb3.conf.cmd b/test/cfg2cmd/spice-usb3.conf.cmd
index 627c077..c515644 100644
--- a/test/cfg2cmd/spice-usb3.conf.cmd
+++ b/test/cfg2cmd/spice-usb3.conf.cmd
@@ -12,7 +12,7 @@
-nodefaults \
-boot 'menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg' \
-vnc unix:/var/run/qemu-server/8006.vnc,password \
- -cpu kvm64,+lahf_lm,+sep,+kvm_pv_unhalt,+kvm_pv_eoi,enforce \
+ -cpu kvm64,enforce,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep \
-m 768 \
-device 'pci-bridge,id=pci.1,chassis_nr=1,bus=pci.0,addr=0x1e' \
-device 'pci-bridge,id=pci.2,chassis_nr=2,bus=pci.0,addr=0x1f' \
diff --git a/test/cfg2cmd/spice-win.conf.cmd b/test/cfg2cmd/spice-win.conf.cmd
index 6f94358..22dfa9d 100644
--- a/test/cfg2cmd/spice-win.conf.cmd
+++ b/test/cfg2cmd/spice-win.conf.cmd
@@ -13,7 +13,7 @@
-boot 'menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg' \
-vnc unix:/var/run/qemu-server/8006.vnc,password \
-no-hpet \
- -cpu 'kvm64,+lahf_lm,+sep,+kvm_pv_unhalt,+kvm_pv_eoi,hv_spinlocks=0x1fff,hv_vapic,hv_time,hv_reset,hv_vpindex,hv_runtime,hv_relaxed,hv_synic,hv_stimer,hv_ipi,enforce' \
+ -cpu 'kvm64,enforce,hv_ipi,hv_relaxed,hv_reset,hv_runtime,hv_spinlocks=0x1fff,hv_stimer,hv_synic,hv_time,hv_vapic,hv_vpindex,+kvm_pv_eoi,+kvm_pv_unhalt,+lahf_lm,+sep' \
-m 768 \
-device 'pci-bridge,id=pci.1,chassis_nr=1,bus=pci.0,addr=0x1e' \
-device 'pci-bridge,id=pci.2,chassis_nr=2,bus=pci.0,addr=0x1f' \
--
2.20.1
More information about the pve-devel
mailing list