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

Stefan Reiter s.reiter at proxmox.com
Mon Sep 30 12:58:45 CEST 2019


* 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.

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.

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 | 112 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 70ed910..20c1061 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -3531,6 +3531,118 @@ 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
+#    however.
+#
+# query_supported_ 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 pmxcfs using PVE::Cluster::get_node_kv('cpuflags').
+#
+sub query_supported_cpu_flags {
+    my $flags = [];
+
+    my $vmid = -1;
+    my $pidfile = pidfile_name($vmid);
+
+    my ($arch, $default_machine) = get_basic_machine_info();
+
+    # FIXME: Once this is merged, the code below should work for ARM without any
+    # further modifications:
+    # 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";
+
+    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($arch),
+	    '-machine', $default_machine,
+	    '-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 => 0);
+	return if $rc;
+
+	eval {
+	    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';
+		}
+	    }
+	};
+	my $err = $@;
+
+	# force stop with 10 sec timeout and 'nocheck'
+	# always stop, even if QMP failed
+	vm_stop(undef, $vmid, 1, 1, 10, 0, 1);
+
+	die $err if $err;
+    });
+
+    # QEMU returns some flags multiple times, with '_', '.' or '-' as separator
+    # (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 query_understood_cpu_flags {
+    my $flags = [];
+    my $flag_section = 0;
+
+    my $arch = get_host_arch();
+
+    # FIXME: Once the patch mentioned in query_supported_cpu_flags is merged,
+    # depending on if the ARM version of query-cpu-model-expansion also returns
+    # non-flags, this function might become irrelevant for ARM entirely.
+    die "CPU Flag detection only supported on x86_64\n"
+	if $arch ne "x86_64";
+
+    run_command(
+	[get_command_for_arch($arch), '-cpu', 'help'],
+	noerr => 1,
+	quiet => 1,
+	outfunc => sub {
+	    my ($line) = @_;
+
+	    if ($flag_section) {
+		return if $line =~ m/^\s*$/;
+		$line =~ s/^\s*|\s*$//g;
+		# Convert all flags to underscore format, since that's what
+		# /proc/cpuinfo and query_supported_cpu_flags use
+		$line =~ s/\.|-/_/g;
+		push @$flags, split(/\s+/, $line);
+	    } elsif ($line =~ m/^\s*Recognized CPUID flags:\s*$/) {
+		$flag_section = 1;
+	    }
+	}
+    );
+
+    return $flags;
+}
+
 sub get_cpu_options {
     my ($conf, $arch, $kvm, $machine_type, $kvm_off, $kvmver, $winversion, $gpu_passthrough) = @_;
 
-- 
2.20.1





More information about the pve-devel mailing list