kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Eduardo Habkost <ehabkost@redhat.com>
To: Valeriy Vdovin <valeriy.vdovin@virtuozzo.com>
Cc: qemu-devel@nongnu.org,
	Marcel Apfelbaum <marcel.apfelbaum@gmail.com>,
	Eric Blake <eblake@redhat.com>,
	Markus Armbruster <armbru@redhat.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	Marcelo Tosatti <mtosatti@redhat.com>,
	Richard Henderson <richard.henderson@linaro.org>,
	Thomas Huth <thuth@redhat.com>,
	Laurent Vivier <lvivier@redhat.com>,
	kvm@vger.kernel.org, Denis Lunev <den@openvz.org>,
	Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Subject: Re: [PATCH v7 1/1] qapi: introduce 'query-cpu-model-cpuid' action
Date: Wed, 26 May 2021 17:44:24 -0400	[thread overview]
Message-ID: <20210526214424.ndk2dwu2crae64y7@habkost.net> (raw)
In-Reply-To: <20210504122639.18342-2-valeriy.vdovin@virtuozzo.com>

On Tue, May 04, 2021 at 03:26:39PM +0300, Valeriy Vdovin wrote:
> Introducing new qapi method 'query-cpu-model-cpuid'. This method can be used to
> get virtualized cpu model info generated by QEMU during VM initialization in
> the form of cpuid representation.
> 
> Diving into more details about virtual cpu generation: QEMU first parses '-cpu'
> command line option. From there it takes the name of the model as the basis for
> feature set of the new virtual cpu. After that it uses trailing '-cpu' options,
> that state if additional cpu features should be present on the virtual cpu or
> excluded from it (tokens '+'/'-' or '=on'/'=off').
> After that QEMU checks if the host's cpu can actually support the derived
> feature set and applies host limitations to it.
> After this initialization procedure, virtual cpu has it's model and
> vendor names, and a working feature set and is ready for identification
> instructions such as CPUID.
> 
> Currently full output for this method is only supported for x86 cpus.
> 
> To learn exactly how virtual cpu is presented to the guest machine via CPUID
> instruction, new qapi method can be used. By calling 'query-cpu-model-cpuid'
> method, one can get a full listing of all CPUID leafs with subleafs which are
> supported by the initialized virtual cpu.
> 
> Other than debug, the method is useful in cases when we would like to
> utilize QEMU's virtual cpu initialization routines and put the retrieved
> values into kernel CPUID overriding mechanics for more precise control
> over how various processes perceive its underlying hardware with
> container processes as a good example.
> 
> Output format:
> The output is a plain list of leaf/subleaf agrument combinations, that
> return 4 words in registers EAX, EBX, ECX, EDX.
> 
> Use example:
> qmp_request: {
>   "execute": "query-cpu-model-cpuid"
> }
> 
> qmp_response: [
>   {
>     "eax": 1073741825,
>     "edx": 77,
>     "leaf": 1073741824,
>     "ecx": 1447775574,
>     "ebx": 1263359563,
>     "subleaf": 0
>   },
>   {
>     "eax": 16777339,
>     "edx": 0,
>     "leaf": 1073741825,
>     "ecx": 0,
>     "ebx": 0,
>     "subleaf": 0
>   },
>   {
>     "eax": 13,
>     "edx": 1231384169,
>     "leaf": 0,
>     "ecx": 1818588270,
>     "ebx": 1970169159,
>     "subleaf": 0
>   },
>   {
>     "eax": 198354,
>     "edx": 126614527,
>   ....
> 
> Signed-off-by: Valeriy Vdovin <valeriy.vdovin@virtuozzo.com>

This breaks --disable-kvm builds (see below[1]), but I like the
simplicity of this solution.

I think it will be an acceptable and welcome mechanism if we name
and document it as KVM-specific.

A debugging command like this that returns the raw CPUID data
directly from the KVM tables would be very useful for automated
testing of our KVM CPUID initialization code.  We have some test
cases for CPU configuration code, but they trust what the CPU
objects tell us and won't catch mistakes in target/i386/kvm.c
CPUID code.

[1] Build error when using --disable-kvm:

  [449/821] Linking target qemu-system-x86_64
  FAILED: qemu-system-x86_64
  c++  -o qemu-system-x86_64 qemu-system-x86_64.p/softmmu_main.c.o libcommon.fa.p/hw_char_virtio-console.c.o [...]
  /usr/bin/ld: libqemu-x86_64-softmmu.fa.p/meson-generated_.._qapi_qapi-commands-machine-target.c.o: in function `qmp_marshal_query_cpu_model_cpuid':
  /home/ehabkost/rh/proj/virt/qemu/build/qapi/qapi-commands-machine-target.c:278: undefined reference to `qmp_query_cpu_model_cpuid'
  collect2: error: ld returned 1 exit status


> 
> ---
> v2: - Removed leaf/subleaf iterators.
>     - Modified cpu_x86_cpuid to return false in cases when count is
>       greater than supported subleaves.
> v3: - Fixed structure name coding style.
>     - Added more comments
>     - Ensured buildability for non-x86 targets.
> v4: - Fixed cpu_x86_cpuid return value logic and handling of 0xA leaf.
>     - Fixed comments.
>     - Removed target check in qmp_query_cpu_model_cpuid.
> v5: - Added error handling code in qmp_query_cpu_model_cpuid
> v6: - Fixed error handling code. Added method to query_error_class
> v7: - Changed implementation in favor of cached cpuid_data for
> KVM_SET_CPUID2
>  qapi/machine-target.json   | 51 ++++++++++++++++++++++++++++++++++++++
>  target/i386/kvm/kvm.c      | 45 ++++++++++++++++++++++++++++++---
>  tests/qtest/qmp-cmd-test.c |  1 +
>  3 files changed, 93 insertions(+), 4 deletions(-)
> 
> diff --git a/qapi/machine-target.json b/qapi/machine-target.json
> index e7811654b7..ad816a50b6 100644
> --- a/qapi/machine-target.json
> +++ b/qapi/machine-target.json
> @@ -329,3 +329,54 @@
>  ##
>  { 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'],
>    'if': 'defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_I386) || defined(TARGET_S390X) || defined(TARGET_MIPS)' }
> +
> +##
> +# @CpuidEntry:
> +#
> +# A single entry of a CPUID response.
> +#
> +# CPUID instruction accepts 'leaf' argument passed in EAX register.
> +# A 'leaf' is a single group of information about the CPU, that is returned
> +# to the caller in EAX, EBX, ECX and EDX registers. A few of the leaves will
> +# also have 'subleaves', the group of information would partially depend on the
> +# value passed in the ECX register. The value of ECX is reflected in the 'subleaf'
> +# field of this structure.
> +#
> +# @leaf: CPUID leaf or the value of EAX prior to CPUID execution.
> +# @subleaf: value of ECX for leaf that has varying output depending on ECX.

Instead of having to explain what "leaf" and "subleaf" means,
maybe it would be simpler to just call them "in_eax" and
"in_ecx"?

> +# @eax: eax
> +# @ebx: ebx
> +# @ecx: ecx
> +# @edx: edx
> +#
> +# Since: 6.1
> +##
> +{ 'struct': 'CpuidEntry',
> +  'data': { 'leaf' : 'uint32',
> +            'subleaf' : 'uint32',

I would make subleaf/in_ecx an optional field.  We don't need to
return it unless KVM_CPUID_FLAG_SIGNIFCANT_INDEX is set.

> +            'eax' : 'uint32',
> +            'ebx' : 'uint32',
> +            'ecx' : 'uint32',
> +            'edx' : 'uint32'
> +          },
> +  'if': 'defined(TARGET_I386)' }
> +
> +##
> +# @query-cpu-model-cpuid:

I would choose a name that indicates that the command is
KVM-specific, like "query-kvm-cpuid" or "query-kvm-cpuid-table".

> +#
> +# Returns description of a virtual CPU model, created by QEMU after cpu
> +# initialization routines. The resulting information is a reflection of a parsed
> +# '-cpu' command line option, filtered by available host cpu features.

I don't think "description of a virtual CPU model" is an accurate
description of this.  I would document it as "returns raw data
from the KVM CPUID table for the first VCPU".

I wonder if the "The resulting information is a reflection of a
parsed [...] cpu features." part is really necessary.  If you
believe people don't understand how "-cpu" works, this is not
exactly the right place to explain that.

If you want to clarify what exactly is returned, maybe something
like the following would work?

  "Returns raw data from the KVM CPUID table for the first VCPU.
  The KVM CPUID table defines the response to the CPUID
  instruction when executed by the guest operating system."



> +#
> +# Returns:  @CpuModelCpuidDescription
> +#
> +# Example:
> +#
> +# -> { "execute": "query-cpu-model-cpuid" }
> +# <- { "return": 'CpuModelCpuidDescription' }
> +#
> +# Since: 6.1
> +##
> +{ 'command': 'query-cpu-model-cpuid',
> +  'returns': ['CpuidEntry'],
> +  'if': 'defined(TARGET_I386)' }
> diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
> index 7fe9f52710..edc4262efb 100644
> --- a/target/i386/kvm/kvm.c
> +++ b/target/i386/kvm/kvm.c
> @@ -20,6 +20,7 @@
>  
>  #include <linux/kvm.h>
>  #include "standard-headers/asm-x86/kvm_para.h"
> +#include "qapi/qapi-commands-machine-target.h"
>  
>  #include "cpu.h"
>  #include "sysemu/sysemu.h"
> @@ -1464,16 +1465,48 @@ static Error *invtsc_mig_blocker;
>  
>  #define KVM_MAX_CPUID_ENTRIES  100
>  
> +struct CPUIDEntriesInfo {
> +    struct kvm_cpuid2 cpuid;
> +    struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES];
> +};

You don't need this new struct definition, as
(&cpuid_data.cpuid.entries[0]) and (&cpuid_data.entries[0]) are
exactly the same.  a kvm_cpuid2 pointer would be enough.

> +
> +struct CPUIDEntriesInfo *cpuid_data_cached;
> +
> +CpuidEntryList *
> +qmp_query_cpu_model_cpuid(Error **errp)
> +{
> +    int i;
> +    struct kvm_cpuid_entry2 *kvm_entry;
> +    CpuidEntryList *head = NULL, **tail = &head;
> +    CpuidEntry *entry;
> +
> +    if (!cpuid_data_cached) {
> +        error_setg(errp, "cpuid_data cache not ready");
> +        return NULL;

I would return a more meaningful error message.  Nobody except
the developers who wrote and reviewed this code knows what
"cpuid_data cache" means.

A message like "VCPU was not initialized yet" would make more
sense.


> +    }
> +
> +    for (i = 0; i < cpuid_data_cached->cpuid.nent; ++i) {
> +        kvm_entry = &cpuid_data_cached->entries[i];
> +        entry = g_malloc0(sizeof(*entry));
> +        entry->leaf = kvm_entry->function;
> +        entry->subleaf = kvm_entry->index;
> +        entry->eax = kvm_entry->eax;
> +        entry->ebx = kvm_entry->ebx;
> +        entry->ecx = kvm_entry->ecx;
> +        entry->edx = kvm_entry->edx;
> +        QAPI_LIST_APPEND(tail, entry);
> +    }
> +
> +    return head;
> +}
> +
>  int kvm_arch_init_vcpu(CPUState *cs)
>  {
> -    struct {
> -        struct kvm_cpuid2 cpuid;
> -        struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES];
> -    } cpuid_data;
>      /*
>       * The kernel defines these structs with padding fields so there
>       * should be no extra padding in our cpuid_data struct.
>       */
> +    struct CPUIDEntriesInfo cpuid_data;
>      QEMU_BUILD_BUG_ON(sizeof(cpuid_data) !=
>                        sizeof(struct kvm_cpuid2) +
>                        sizeof(struct kvm_cpuid_entry2) * KVM_MAX_CPUID_ENTRIES);
> @@ -1833,6 +1866,10 @@ int kvm_arch_init_vcpu(CPUState *cs)
>      if (r) {
>          goto fail;
>      }
> +    if (!cpuid_data_cached) {
> +        cpuid_data_cached = g_malloc0(sizeof(cpuid_data));
> +        memcpy(cpuid_data_cached, &cpuid_data, sizeof(cpuid_data));

You are going to copy more entries than necessary, but on the
other hand I like the simplicity of not having to calculate the
struct size before allocating.


> +    }

Now I'm wondering if we want to cache the CPUID tables for all
VCPUs (not just the first one).

Being a debugging command, maybe it's an acceptable compromise to
copy the data only from one VCPU.  If the need to return data for
other VCPUs arise, we can extend the command later.


>  
>      if (has_xsave) {
>          env->xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave));
> diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c
> index c98b78d033..f5a926b61b 100644
> --- a/tests/qtest/qmp-cmd-test.c
> +++ b/tests/qtest/qmp-cmd-test.c
> @@ -46,6 +46,7 @@ static int query_error_class(const char *cmd)
>          { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE },
>          { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR },
>          { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR },
> +        { "query-cpu-model-cpuid", ERROR_CLASS_GENERIC_ERROR },
>          { NULL, -1 }
>      };
>      int i;
> -- 
> 2.17.1
> 

-- 
Eduardo


  reply	other threads:[~2021-05-26 21:44 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-04 12:26 [PATCH v7 0/1] qapi: introduce 'query-cpu-model-cpuid' action Valeriy Vdovin
2021-05-04 12:26 ` [PATCH v7 1/1] " Valeriy Vdovin
2021-05-26 21:44   ` Eduardo Habkost [this message]
2021-05-31 12:16     ` Valeriy Vdovin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210526214424.ndk2dwu2crae64y7@habkost.net \
    --to=ehabkost@redhat.com \
    --cc=armbru@redhat.com \
    --cc=den@openvz.org \
    --cc=eblake@redhat.com \
    --cc=kvm@vger.kernel.org \
    --cc=lvivier@redhat.com \
    --cc=marcel.apfelbaum@gmail.com \
    --cc=mtosatti@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=richard.henderson@linaro.org \
    --cc=thuth@redhat.com \
    --cc=valeriy.vdovin@virtuozzo.com \
    --cc=vsementsov@virtuozzo.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).