[pve-devel] [PATCH v3 qemu-server 04/12] Add QEMU CPU flag querying helpers

Fabian Grünbichler f.gruenbichler at proxmox.com
Wed Oct 23 10:29:45 CEST 2019


On October 15, 2019 4:12 pm, Stefan Reiter wrote:
> * query_understood_cpu_flags returns all flags that QEMU/KVM knows about
> * query_supported_cpu_flags returns all flags that QEMU/KVM can use on
>   this particular host.
> 
> To get supported flags, a temporary VM is started with QEMU, so we can
> issue the "query-cpu-model-expansion" QMP command. This is how libvirt
> queries supported flags for its "host-passthrough" CPU type.
> query_supported_cpu_flags is thus rather slow and shouldn't be called
> unnecessarily.
> 
> Note that KVM and TCG accelerators provide different expansions for the
> "host" CPU type, so we need to query both.
> 
> Currently only supports x86_64, because QEMU-aarch64 doesn't provide the
> necessary querying functions.
> 
> Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
> ---
> 
> query_understood_cpu_flags is currently not used, but will be very useful for
> the later UI part. I think it thematically fits best with this patch though.
> 
> v2 -> v3:
> * support tcg accelerator as well as KVM (only query TCG on hosts without KVM)
> * change query_understood_cpu_flags to read the static file generated in 02/11
> * simplify s/\.|-/_/g and dedup code
> * $vmid -> $fakevmid
> * better comments
> 
> v1 -> v2:
> * Change order of functions and add a single, more useful comment on usage
> * Do s/\.|-/_/g directly in query_understood_cpu_flags, since the other format
>   is useless anyway
> * Add "die" and FIXME for aarch64, since it doesn't support querying atm
>   (still, use get_host_arch()/get_basic_machine_info() for now, so once QEMU
>   supports it, we theoretically just have to remove the "die")
> * Do QMP in extra eval, so we don't die before calling vm_stop
> 
> 
>  PVE/QemuServer.pm | 117 +++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 116 insertions(+), 1 deletion(-)
> 
> diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
> index 8376260..4447afc 100644
> --- a/PVE/QemuServer.pm
> +++ b/PVE/QemuServer.pm
> @@ -24,7 +24,7 @@ use Storable qw(dclone);
>  use MIME::Base64;
>  use PVE::Exception qw(raise raise_param_exc);
>  use PVE::Storage;
> -use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline dir_glob_foreach $IPV6RE);
> +use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline file_get_contents dir_glob_foreach $IPV6RE);
>  use PVE::JSONSchema qw(get_standard_option);
>  use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
>  use PVE::INotify;
> @@ -3534,6 +3534,121 @@ sub get_command_for_arch($) {
>      return $cmd;
>  }
>  
> +# To use query_supported_cpu_flags and query_understood_cpu_flags to get flags
> +# to use in a QEMU command line (-cpu element), first array_intersect the result
> +# of query_supported_ with query_understood_. This is necessary because:
> +#
> +# a) query_understood_ returns flags the host cannot use and
> +# b) query_supported_ (rather the QMP call) doesn't actually return CPU
> +#    flags, but CPU settings - with most of them being flags. Those settings
> +#    (and some flags, curiously) cannot be specified as a "-cpu" argument.
> +#
> +# query_supported_ needs to start up to 2 temporary VMs and is therefore rather
> +# expensive. If you need the value returned from this, you can get it much
> +# cheaper from pmxcfs using PVE::Cluster::get_node_kv('cpuflags').

this comment is now outdated, since pvestatd broadcasts cpuflags-tcg and 
cpuflags-kvm

> +#
> +# pvestatd calls this function on startup and whenever the QEMU/KVM version
> +# changes, automatically populating pmxcfs.
> +#
> +# Returns: { kvm => [ flagX, flagY, ... ], tcg => [ flag1, flag2, ... ] }
> +# since kvm and tcg machines support different flags
> +#
> +sub query_supported_cpu_flags {
> +    my $flags = {};
> +
> +    my ($arch, $default_machine) = get_basic_machine_info();
> +
> +    # FIXME: Once this is merged, the code below should work for ARM as well:
> +    # https://lists.nongnu.org/archive/html/qemu-devel/2019-06/msg04947.html
> +    die "QEMU/KVM cannot detect CPU flags on ARM (aarch64)\n" if
> +	$arch eq "aarch64";
> +
> +    my $kvm_supported = defined(kvm_version());
> +    my $qemu_cmd = get_command_for_arch($arch);
> +    my $fakevmid = -1;
> +    my $pidfile = pidfile_name($fakevmid);
> +
> +    # Start a temporary (frozen) VM with vmid -1 to allow sending a QMP command
> +    my $query_supported_run_qemu = sub {
> +	my ($kvm) = @_;
> +
> +	my $flags = {};
> +	my $cmd = [
> +	    $qemu_cmd,
> +	    '-machine', $default_machine,
> +	    '-display', 'none',
> +	    '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server,nowait",
> +	    '-mon', 'chardev=qmp,mode=control',
> +	    '-pidfile', $pidfile,
> +	    '-S', '-daemonize'
> +	];
> +
> +	if (!$kvm) {
> +	    push @$cmd, '-accel', 'tcg';
> +	}
> +
> +	my $rc = run_command($cmd, noerr => 1, quiet => 0);
> +	die "QEMU flag querying VM exited with code " . $rc if $rc;
> +
> +	eval {
> +	    my $cmd_result = vm_mon_cmd_nocheck(
> +		$fakevmid,
> +		'query-cpu-model-expansion',
> +		type => 'full',
> +		model => { name => 'host' }
> +	    );
> +
> +	    my $props = $cmd_result->{model}->{props};
> +	    foreach my $prop (keys %$props) {
> +		next if $props->{$prop} ne '1';
> +		# QEMU returns some flags multiple times, with '_', '.' or '-'
> +		# (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...).
> +		# We only keep those with underscores, to match /proc/cpuinfo
> +		$prop =~ s/\.|-/_/g;
> +		$flags->{$prop} = 1;
> +	    }
> +	};
> +	my $err = $@;
> +
> +	# force stop with 10 sec timeout and 'nocheck'
> +	# always stop, even if QMP failed
> +	vm_stop(undef, $fakevmid, 1, 1, 10, 0, 1);
> +
> +	die $err if $err;
> +
> +	return [ keys %$flags ];

sort keys ? sorting once here seems better than sorting when using it 
later ;)

> +    };
> +
> +    # We need to query QEMU twice, since KVM and TCG have different supported flags
> +    PVE::QemuConfig->lock_config($fakevmid, sub {
> +	$flags->{tcg} = eval { $query_supported_run_qemu->(0) };
> +	warn "warning: failed querying supported tcg flags: $@\n" if $@;
> +
> +	if ($kvm_supported) {
> +	    $flags->{kvm} = eval { $query_supported_run_qemu->(1) };
> +	    warn "warning: failed querying supported kvm flags: $@\n" if $@;
> +	}
> +    });
> +
> +    return $flags;
> +}
> +
> +# Understood CPU flags are written to a file at 'pve-qemu' compile time
> +my $understood_cpu_flag_dir = "/usr/share/kvm";
> +sub query_understood_cpu_flags {
> +    my $arch = get_host_arch();
> +    my $filepath = "$understood_cpu_flag_dir/cpu-flags-understood-$arch";
> +
> +    die "Cannot query understood QEMU CPU flags for architecture: $arch (file not found)\n"
> +	if ! -e $filepath;
> +
> +    my $raw = file_get_contents($filepath);
> +    $raw =~ s/^\s+|\s+$//g;
> +    my @flags = split(/\s+/, $raw);
> +
> +    return \@flags;
> +}
> +
>  sub get_cpu_options {
>      my ($conf, $arch, $kvm, $machine_type, $kvm_off, $kvmver, $winversion, $gpu_passthrough) = @_;
>  
> -- 
> 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