* 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. Signed-off-by: Stefan Reiter <s.rei...@proxmox.com> --- Changes from RFC: * Clearer regexes * Use existing QMP infrastructure * Add locking for temporary VM start * Add comments PVE/QemuServer.pm | 84 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 8c519b5..97fa955 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3500,6 +3500,90 @@ sub get_command_for_arch($) { return $cmd; } +# The format of the flags returned here uses dashes '-' as seperators, +# this is neither the default for 'query_supported_cpu_flags' below, nor for +# /proc/cpuinfo. +# +# To compare (or array_intersect) flags, it's a good idea to convert them all +# to a common format first (e.g. map s/\.|-/_/g). +sub query_understood_cpu_flags { + my $flags = []; + my $flag_section = 0; + + run_command( + [get_command_for_arch('x86_64'), '-cpu', 'help'], + noerr => 1, + quiet => 1, + outfunc => sub { + my ($line) = @_; + + if ($flag_section) { + return if $line =~ m/^\s*$/; + $line =~ s/^\s*|\s*$//g; + push @$flags, split(/\s+/, $line); + } elsif ($line =~ m/^\s*Recognized CPUID flags:\s*$/) { + $flag_section = 1; + } + } + ); + + return $flags; +} + +# This function needs to start a temporary VM and is therefore rather expensive. +# If you need the value returned from this, you can get it much cheaper from +# pvestatd using PVE::Cluster::get_node_kv('cpuflags'). +# +# See also note to query_understood_cpu_flags above. +sub query_supported_cpu_flags { + my $flags = []; + + my $vmid = -1; + my $pidfile = pidfile_name($vmid); + + PVE::QemuConfig->lock_config($vmid, sub { + # We start a temporary (frozen) VM with vmid -1 to allow us to send a QMP command + my $rc = run_command([ + get_command_for_arch('x86_64'), + '-cpu', 'kvm64', + '-display', 'none', + '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$vmid.qmp,server,nowait", + '-mon', 'chardev=qmp,mode=control', + '-pidfile', $pidfile, + '-S', '-daemonize' + ], noerr => 1, quiet => 1); + return if $rc; + + my $cmd_result = vm_mon_cmd_nocheck( + $vmid, + 'query-cpu-model-expansion', + type => 'full', + model => { name => 'host' } + ); + + my $props = $cmd_result->{model}->{props}; + if (%$props) { + foreach my $prop (keys %$props) { + push @$flags, $prop if "$props->{$prop}" eq '1'; + } + } + + # force stop with 10 sec timeout and 'nocheck' + vm_stop(undef, $vmid, 1, 1, 10, 0, 1); + }); + + # QEMU returns some flags multiple times, with '_', '.' or '-' as seperator + # (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...). + # We only keep those with underscores, since they match the ones from + # /proc/cpuinfo (they do the same thing, but we get rid of duplicates). + @$flags = grep { + my $replaced = (my $underscore = $_) =~ s/\.|-/_/g; + !($replaced && grep { $_ eq $underscore } @$flags) + } @$flags; + + 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@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel