[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