qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v2] target/i386: add "-cpu, lbr-fmt=*" support to enable gues


From: Eduardo Habkost
Subject: Re: [PATCH v2] target/i386: add "-cpu, lbr-fmt=*" support to enable guest LBR
Date: Wed, 28 Apr 2021 17:19:08 -0400

On Tue, Apr 27, 2021 at 04:09:48PM +0800, Like Xu wrote:
> The last branch recording (LBR) is a performance monitor unit (PMU)
> feature on Intel processors that records a running trace of the most
> recent branches taken by the processor in the LBR stack. The QEMU
> could configure whether it's enabled or not for each guest via CLI.
> 
> The LBR feature would be enabled on the guest if:
> - the KVM is enabled and the PMU is enabled and,
> - the msr-based-feature IA32_PERF_CAPABILITIES is supporterd on KVM and,
> - the supported returned value for lbr_fmt from this msr is not zero and,
> - the requested guest vcpu model does support FEAT_1_ECX.CPUID_EXT_PDCM,
> - the configured lbr-fmt value is the same as the host lbr_fmt value
>   OR use the QEMU option "-cpu host,migratable=no".

I don't understand why "migratable" matters here.  "migratable"
is just a convenience property to get better defaults when using
"-cpu host".  I don't know why it would change the lbr-fmt
validation rules.

> 
> Signed-off-by: Like Xu <like.xu@linux.intel.com>
> ---

A changelog explaining what you changed since v1 would have been
useful here.

>  target/i386/cpu.c     | 34 ++++++++++++++++++++++++++++++++++
>  target/i386/cpu.h     | 10 ++++++++++
>  target/i386/kvm/kvm.c | 10 ++++++++--
>  3 files changed, 52 insertions(+), 2 deletions(-)
> 
> diff --git a/target/i386/cpu.c b/target/i386/cpu.c
> index ad99cad0e7..9c8e54aa6f 100644
> --- a/target/i386/cpu.c
> +++ b/target/i386/cpu.c
> @@ -6623,6 +6623,10 @@ static void x86_cpu_filter_features(X86CPU *cpu, bool 
> verbose)
>      }
>  
>      for (w = 0; w < FEATURE_WORDS; w++) {
> +        if (w == FEAT_PERF_CAPABILITIES) {
> +            continue;
> +        }
> +

Why exactly is this necessary?  I expected to be completely OK to
call mark_unavailable_features() multiple times for the same
FeatureWord.

If there's a reason why this is necessary, I suggest adding a
comment explaining why.

>          uint64_t host_feat =
>              x86_cpu_get_supported_feature_word(w, false);
>          uint64_t requested_features = env->features[w];
> @@ -6630,6 +6634,27 @@ static void x86_cpu_filter_features(X86CPU *cpu, bool 
> verbose)
>          mark_unavailable_features(cpu, w, unavailable_features, prefix);
>      }
>  
> +    uint64_t host_perf_cap =
> +        x86_cpu_get_supported_feature_word(FEAT_PERF_CAPABILITIES, false);
> +    if (!cpu->lbr_fmt && !cpu->migratable) {
> +        cpu->lbr_fmt = host_perf_cap & PERF_CAP_LBR_FMT;

"migratable=no" is not a request to override explicit user
settings.  This is why we have the ~env->user_features masking
inside x86_cpu_expand_features() when initializing
env->features[].

In either case, I don't understand why you need the lines above.
"migratable=no" should already trigger the x86_cpu_get_supported_feature_word()
loop inside x86_cpu_expand_features(), and it should initialize
env->features[FEAT_PERF_CAPABILITIES] with the host value.  Isn't
that code working for you?


> +        if (cpu->lbr_fmt) {
> +            info_report("vPMU: The value of lbr-fmt has been adjusted "
> +                        "to 0x%lx and guest LBR is enabled.",
> +                        host_perf_cap & PERF_CAP_LBR_FMT);


>From your other message:

(I'm assuming your examples are for a lbr-fmt=5 host)

> "-cpu host,migratable=no" --> "Enable guest LBR and show warning"

Enabling guest LBR in this case is 100% OK, isn't it?  I don't
think you need to show a warning.


> "-cpu host,migratable=no,lbr-fmt=0" --> "Enable guest LBR and show warning"

Why?  In this case, we should do what the user asked for whenever
possible, and the user is explicitly asking lbr-fmt to be 0.

> "-cpu host,migratable=no,lbr-fmt=5" --> "Enable guest LBR"

Looks OK.

> "-cpu host,migratable=no,lbr-fmt=6" --> "Disable guest LBR and show warning"

Makes sense to me[1].


> +        }
> +    } else {
> +        uint64_t requested_lbr_fmt = cpu->lbr_fmt & PERF_CAP_LBR_FMT;
> +        if (requested_lbr_fmt && kvm_enabled()) {


>From your other message:

> "-cpu host,lbr-fmt=0" --> "Disable guest LBR"

Makes sense to me.  I understand this as a confirmation that it's
OK to have a guest/host mismatch if guest LBR=0.

> "-cpu host,lbr-fmt=5" --> "Enable guest LBR"

Makes sense to me.

> "-cpu host,lbr-fmt=6" --> "Disable guest LBR and show warning"

Makes sense to me[1].


[1] As long as "show warning" becomes "fatal error" if enforce=1.
    mark_unavailable_features() should make sure this happens.

    Or maybe we should make this an error?  It would be even
    better.  The example code below makes it an error.


> +            if (requested_lbr_fmt != (host_perf_cap & PERF_CAP_LBR_FMT)) {
> +                cpu->lbr_fmt = 0;
> +                warn_report("vPMU: The supported lbr-fmt value on the host "
> +                            "is 0x%lx and guest LBR is disabled.",
> +                            host_perf_cap & PERF_CAP_LBR_FMT);
> +            }
> +        }
> +    }
> +
>      if ((env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) &&
>          kvm_enabled()) {
>          KVMState *s = CPU(cpu)->kvm_state;
> @@ -6734,6 +6759,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error 
> **errp)
>          }
>      }
>  
> +    if (cpu->lbr_fmt) {
> +        if (!cpu->enable_pmu) {
> +            error_setg(errp, "LBR is unsupported since guest PMU is 
> disabled.");
> +            return;
> +        }
> +        env->features[FEAT_PERF_CAPABILITIES] |= cpu->lbr_fmt;

This doesn't seem right, as we should still do what the user
asked for if "lbr-fmt=0" is used.

You need to differentiate "lbr-fmt=0" from "lbr-fmt not set"
somehow.  I suggest initializing lbr_fmt to 0xFF by default,
so you can override env->features[FEAT_PERF_CAPABILITIES]
when lbr_fmt != 0xFF (even if lbr_fmt=0).

Something like this:

  #define LBR_FMT_UNSET 0xff
  ...
  DEFINE_PROP_UINT8("lbr-fmt", X86CPU, lbr_fmt, LBR_FMT_UNSET)
  ...

  void x86_cpu_realizefn(...)
  {
      ...
      if (cpu->lbr_fmt != LBR_FMT_UNSET) {
          if ((cpu->lbr_fmt & LBR_FMT_FMT) != cpu->lbr_fmt) {
              error_setg(errp, "invalid lbr-fmt" ...);
              return;
          }
          env->features[FEAT_PERF_CAPABILITIES] &= ~PERF_CAP_LBR_FMT;
          env->features[FEAT_PERF_CAPABILITIES] |= cpu->lbr_fmt;
      }
      /* If lbr_fmt == LBR_FMT_UNSET, the default value of env->features[]
       * will be used as is (and it may depend on the "migratable" flag)
       */
      ...
      /*
       * We can always validate env->features[FEAT_PERF_CAPABILITIES],
       * no matter how it was initialized:
       */
      uint64_t requested_lbr_fmt =
          env->features[FEAT_PERF_CAPABILITIES] & PERF_CAP_LBR_FMT;
      if (requested_lbr_fmt && kvm_enabled()) {
          /* Maybe this code will work out of the box for all
           * accelerators, but checking kvm_enabled() is safer.
           */
          uint64_t host_perf_cap =
              x86_cpu_get_supported_feature_word(FEAT_PERF_CAPABILITIES, false);
          uint64_t host_lbr_fmt = host_perf_cap & PERF_CAP_LBR_FMT;
          if (!cpu->enable_pmu) {
              error_setg(errp, "LBR is unsupported without pmu=on");
              return;
          }
          if (requested_lbr_fmt != host_lbr_fmt)) {
              /* An error is better than a warning */
              error_setg(errp, "lbr-fmt mismatch" ...);
              /* probably a good idea to include requested_lbr_fmt
               * and host_lbr_fmt in the error message */
              return;
          }
      }
      ...
  }



> +    }
> +
>      /* mwait extended info: needed for Core compatibility */
>      /* We always wake on interrupt even if host does not have the capability 
> */
>      cpu->mwait.ecx |= CPUID_MWAIT_EMX | CPUID_MWAIT_IBE;
> @@ -7300,6 +7333,7 @@ static Property x86_cpu_properties[] = {
>  #endif
>      DEFINE_PROP_INT32("node-id", X86CPU, node_id, CPU_UNSET_NUMA_NODE_ID),
>      DEFINE_PROP_BOOL("pmu", X86CPU, enable_pmu, false),
> +    DEFINE_PROP_UINT8("lbr-fmt", X86CPU, lbr_fmt, 0),
>  
>      DEFINE_PROP_UINT32("hv-spinlocks", X86CPU, hyperv_spinlock_attempts,
>                         HYPERV_SPINLOCK_NEVER_NOTIFY),
> diff --git a/target/i386/cpu.h b/target/i386/cpu.h
> index 570f916878..b12c879fc4 100644
> --- a/target/i386/cpu.h
> +++ b/target/i386/cpu.h
> @@ -354,6 +354,7 @@ typedef enum X86Seg {
>  #define ARCH_CAP_TSX_CTRL_MSR                (1<<7)
>  
>  #define MSR_IA32_PERF_CAPABILITIES      0x345
> +#define PERF_CAP_LBR_FMT      0x3f
>  
>  #define MSR_IA32_TSX_CTRL            0x122
>  #define MSR_IA32_TSCDEADLINE            0x6e0
> @@ -1726,6 +1727,15 @@ struct X86CPU {
>       */
>      bool enable_pmu;
>  
> +    /*
> +     * Configure LBR_FMT bits on IA32_PERF_CAPABILITIES MSR.
> +     * This can't be enabled by default yet because it doesn't have
> +     * ABI stability guarantees, as it is only allowed to pass all
> +     * LBR_FMT bits returned by kvm_arch_get_supported_msr_feature()
> +     * (that depends on host CPU and kernel capabilities) to the guest.
> +     */
> +    uint8_t lbr_fmt;
> +
>      /* LMCE support can be enabled/disabled via cpu option 'lmce=on/off'. It 
> is
>       * disabled by default to avoid breaking migration between QEMU with
>       * different LMCE configurations.
> diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
> index 7fe9f52710..aa926984ae 100644
> --- a/target/i386/kvm/kvm.c
> +++ b/target/i386/kvm/kvm.c
> @@ -2732,8 +2732,14 @@ static void kvm_msr_entry_add_perf(X86CPU *cpu, 
> FeatureWordArray f)
>                                             MSR_IA32_PERF_CAPABILITIES);
>  
>      if (kvm_perf_cap) {
> -        kvm_msr_entry_add(cpu, MSR_IA32_PERF_CAPABILITIES,
> -                        kvm_perf_cap & f[FEAT_PERF_CAPABILITIES]);
> +        kvm_perf_cap = (cpu->migratable) ?
> +            (kvm_perf_cap & f[FEAT_PERF_CAPABILITIES]) : kvm_perf_cap;

I don't understand why you are checking cpu->migratable here.
The CPU code should ensure f[FEAT_PERF_CAPABILITIES] is
initialized correctly before calling kvm_arch_init_vcpu().

> +
> +        if (!cpu->lbr_fmt) {
> +            kvm_perf_cap &= ~PERF_CAP_LBR_FMT;
> +        }

Likewise: this should be done by the CPU initialization code
before kvm_arch_init_vcpu() gets called.

The existing code looks weird here: what's the purpose of the
kvm_arch_get_supported_msr_feature() call in this function?

env->features[] is supposed to reflect what the guest actually
sees.  x86_cpu_realizefn()/x86_cpu_filter_features() is supposed
to ensure that before calling kvm_arch_init_vcpu().  If there's a
mismatch between env->features and what the guest sees, we have a
problem.

If you want to be 100% sure, maybe you can add an assert() here.
But if the function is receiving invalid input it's too late to
fix the value.

> +
> +        kvm_msr_entry_add(cpu, MSR_IA32_PERF_CAPABILITIES, kvm_perf_cap);
>      }
>  }
>  
> -- 
> 2.30.2
> 

-- 
Eduardo




reply via email to

[Prev in Thread] Current Thread [Next in Thread]