[pve-devel] [PATCH v3 qemu-server 10/12] Rework get_cpu_options and allow custom CPU models
Fabian Grünbichler
f.gruenbichler at proxmox.com
Wed Oct 23 10:30:38 CEST 2019
On October 15, 2019 4:12 pm, Stefan Reiter wrote:
> 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>
> ---
>
> v4 -> v5:
> * variable renaming
> * extract parse_cpuflag_list sub to enable better code reuse
>
> v3 -> v4:
> * use is_custom_model
>
> v3: Since it's just a few lines, I included the test changes into this patch
> directly. This way all patches in the series should be buildable without
> problems.
>
> v2: It was quite interesting to dig through old commit messages/mail archives to
> find the actual reasons some of the hardcoded flags are included. I do feel like
> the "reason" field is quite useful though, both for future developers and users.
>
> PVE/QemuServer/CPUConfig.pm | 192 +++++++++++++++------
> test/cfg2cmd/i440fx-win10-hostpci.conf.cmd | 2 +-
> test/cfg2cmd/minimal-defaults.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-usb3.conf.cmd | 2 +-
> 7 files changed, 147 insertions(+), 57 deletions(-)
>
> diff --git a/PVE/QemuServer/CPUConfig.pm b/PVE/QemuServer/CPUConfig.pm
> index eaa4f37..583003c 100644
> --- a/PVE/QemuServer/CPUConfig.pm
> +++ b/PVE/QemuServer/CPUConfig.pm
> @@ -360,100 +360,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, $machine_type, $kvm_off, $kvmver, $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 $conf->{ostype} && $conf->{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'};
this is missing or set default. reported-model is optional, but $cputype
is seen as defined from this point on. otherwise an invalid or absent
reported-model line leads to lots of "uninitialized" warnings followed
by "internal error" ;)
> + $kvm_off = $custom_cpu->{hidden}
> + if defined($custom_cpu->{hidden});
> + $hv_vendor_id = $custom_cpu->{'hv-vendor-id'};
> + }
>
> - if (qemu_machine_feature_enabled ($machine_type, $kvmver, 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_type, $kvmver);
> +
> + my $hv_flags = get_hyperv_enlightenments($winversion, $machine_type, $kvmver,
> + $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_type, $kvmver, $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_type, $kvmver) = @_;
> +
> + 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 (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)
> + && $arch eq 'x86_64' && $kvm) {
> + $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_type, $kvmver, $bios, $gpu_passthrough, $hv_vendor_id) = @_;
> +sub get_hyperv_enlightenments {
> + my ($winversion, $machine_type, $kvmver, $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 (qemu_machine_feature_enabled ($machine_type, $kvmver, 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 (qemu_machine_feature_enabled ($machine_type, $kvmver, 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 (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 12)) {
> - push @$cpuFlags , 'hv_synic';
> - push @$cpuFlags , 'hv_stimer';
> + $flagfn->('hv_synic', undef, $win7_reason);
> + $flagfn->('hv_stimer', undef, $win7_reason);
> }
>
> if (qemu_machine_feature_enabled ($machine_type, $kvmver, 3, 1)) {
> - push @$cpuFlags , 'hv_ipi';
> + $flagfn->('hv_ipi', undef, $win7_reason);
> }
> }
> +
> + return $flags;
> }
>
> sub qemu_machine_feature_enabled {
> diff --git a/test/cfg2cmd/i440fx-win10-hostpci.conf.cmd b/test/cfg2cmd/i440fx-win10-hostpci.conf.cmd
> index ff5d635..2a9174d 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 5abebe9..444050b 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/q35-linux-hostpci.conf.cmd b/test/cfg2cmd/q35-linux-hostpci.conf.cmd
> index 21fb18b..2072295 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 f2c08ca..81e43d4 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-usb3.conf.cmd b/test/cfg2cmd/spice-usb3.conf.cmd
> index 680fa64..66b4e8d 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.2,chassis_nr=2,bus=pci.0,addr=0x1f' \
> -device 'pci-bridge,id=pci.1,chassis_nr=1,bus=pci.0,addr=0x1e' \
> --
> 2.20.1
>
>
> _______________________________________________
> pve-devel mailing list
> pve-devel at pve.proxmox.com
> https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
>
>
More information about the pve-devel
mailing list