From: Peter Zijlstra <peterz@infradead.org>
To: kan.liang@intel.com
Cc: andi@firstfloor.org, linux-kernel@vger.kernel.org, kvm@vger.kernel.org
Subject: Re: [PATCH V5 1/2] perf ignore LBR and extra_regs
Date: Mon, 14 Jul 2014 12:53:41 +0200 [thread overview]
Message-ID: <20140714105341.GT9918@twins.programming.kicks-ass.net> (raw)
In-Reply-To: <1404989984-3068-1-git-send-email-kan.liang@intel.com>
[-- Attachment #1: Type: text/plain, Size: 5658 bytes --]
On Thu, Jul 10, 2014 at 03:59:43AM -0700, kan.liang@intel.com wrote:
> From: Kan Liang <kan.liang@intel.com>
>
> x86, perf: Protect LBR and extra_regs against KVM lying
>
> With -cpu host, KVM reports LBR and extra_regs support, if the host has support.
> When the guest perf driver tries to access LBR or extra_regs MSR,
> it #GPs all MSR accesses,since KVM doesn't handle LBR and extra_regs support.
> So check the related MSRs access right once at initialization time to avoid the error access at runtime.
>
> For reproducing the issue, please build the kernel with CONFIG_KVM_INTEL = y (for host kernel).
> And CONFIG_PARAVIRT = n and CONFIG_KVM_GUEST = n (for guest kernel).
> Start the guest with -cpu host.
> Run perf record with --branch-any or --branch-filter in guest to trigger LBR #GP.
> Run perf stat offcore events (E.g. LLC-loads/LLC-load-misses ...) in guest to trigger offcore_rsp #GP
This is still not properly wrapped at 78 chars.
> Signed-off-by: Kan Liang <kan.liang@intel.com>
>
> V2: Move the check code to initialization time.
> V3: Add flag for each extra register.
> Check all LBR MSRs at initialization time.
> V4: Remove lbr_msr_access. For LBR msr, simply set lbr_nr to 0 if check_msr failed.
> Disable all extra msrs in creation places if check_msr failed.
> V5: Fix check_msr broken
> Don't check any more MSRs after the first fail
> Return error when checking fail to stop creating the event
> Remove the checking code path which never get
These things should go below the --- so they get thrown away when
applying the patch, its of no relevance once applied.
> ---
> arch/x86/kernel/cpu/perf_event.c | 3 +++
> arch/x86/kernel/cpu/perf_event.h | 45 ++++++++++++++++++++++++++++++++++
> arch/x86/kernel/cpu/perf_event_intel.c | 38 +++++++++++++++++++++++++++-
> 3 files changed, 85 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
> index 2bdfbff..a7c5e4b 100644
> --- a/arch/x86/kernel/cpu/perf_event.c
> +++ b/arch/x86/kernel/cpu/perf_event.c
> @@ -118,6 +118,9 @@ static int x86_pmu_extra_regs(u64 config, struct perf_event *event)
> continue;
> if (event->attr.config1 & ~er->valid_mask)
> return -EINVAL;
> + /* Check if the extra msrs can be safely accessed*/
> + if (!x86_pmu.extra_msr_access[er->idx])
> + return -EFAULT;
This is not a correct usage of -EFAULT. Event creation did not fail
because we took a fault dereferencing a user provided pointer. Possibly
ENXIO is appropriate.
> reg->idx = er->idx;
> reg->config = event->attr.config1;
> diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h
> index 3b2f9bd..992c678 100644
> --- a/arch/x86/kernel/cpu/perf_event.h
> +++ b/arch/x86/kernel/cpu/perf_event.h
> @@ -464,6 +464,12 @@ struct x86_pmu {
> */
> struct extra_reg *extra_regs;
> unsigned int er_flags;
> + /*
> + * EXTRA REG MSR can be accessed
> + * The extra registers are completely unrelated to each other.
> + * So it needs a flag for each extra register.
> + */
> + bool extra_msr_access[EXTRA_REG_MAX];
So why not in struct extra_reg again? You didn't give a straight answer
there.
> +/*
> + * Under certain circumstances, access certain MSR may cause #GP.
> + * The function tests if the input MSR can be safely accessed.
> + */
> +static inline bool check_msr(unsigned long msr)
> +{
This reads like a generic function;
> + u64 val_old, val_new, val_tmp;
> +
> + /*
> + * Read the current value, change it and read it back to see if it
> + * matches, this is needed to detect certain hardware emulators
> + * (qemu/kvm) that don't trap on the MSR access and always return 0s.
> + */
> + if (rdmsrl_safe(msr, &val_old))
> + goto msr_fail;
> + /*
> + * Only chagne it slightly,
> + * since the higher bits of some MSRs cannot be updated by wrmsrl.
> + * E.g. MSR_LBR_TOS
> + */
> + val_tmp = val_old ^ 0x3UL;
but this is not generally true; not all MSRs can write the 2 LSB, can
they? One option would be to extend the function with a u64 mask.
> + if (wrmsrl_safe(msr, val_tmp) ||
> + rdmsrl_safe(msr, &val_new))
> + goto msr_fail;
> +
> + if (val_new != val_tmp)
> + goto msr_fail;
> +
> + /* Here it's sure that the MSR can be safely accessed.
> + * Restore the old value and return.
> + */
> + wrmsrl(msr, val_old);
> +
> + return true;
> +
> +msr_fail:
> + return false;
> +}
Also, by now this function is far too large to be inline and in a
header.
> + /*
> + * Access LBR MSR may cause #GP under certain circumstances.
> + * E.g. KVM doesn't support LBR MSR
> + * Check all LBT MSR here.
> + * Disable LBR access if any LBR MSRs can not be accessed.
> + */
> + if (x86_pmu.lbr_nr) {
> + if (check_msr(x86_pmu.lbr_tos)) {
> + for (i = 0; i < x86_pmu.lbr_nr; i++) {
> + if (!(check_msr(x86_pmu.lbr_from + i) &&
> + check_msr(x86_pmu.lbr_to + i))) {
> + x86_pmu.lbr_nr = 0;
> + break;
> + }
> + }
> + } else
> + x86_pmu.lbr_nr = 0;
That's needlessly complex and indented.
if (x86_pmu.lbr_nr && !check_msr(x86_pmu.lbr_tos)
x86_pmu.lbr_nr = 0;
for (i = 0; i < x86_pmu.lbr_nr; i++) {
if (!(check_msr(x86_pmu.lbr_from + i) &&
check_msr(x86_pmu.lbr_to + i)))
x86_pmu.lbr_nr = 0;
}
You don't need to wrap the for loop in a lbr_nr test and you don't need
a break to terminate. Once you set lbr_nr = 0, the for loop will
terminate on its own. If it was already 0 it would've never started.
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
next prev parent reply other threads:[~2014-07-14 10:53 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-07-10 10:59 [PATCH V5 1/2] perf ignore LBR and extra_regs kan.liang
2014-07-10 10:59 ` [PATCH V5 2/2] kvm: ignore LBR and extra_reg kan.liang
2014-07-14 10:53 ` Peter Zijlstra [this message]
2014-07-14 14:28 ` [PATCH V5 1/2] perf ignore LBR and extra_regs Liang, Kan
2014-07-14 16:28 ` Peter Zijlstra
2014-07-14 11:08 ` Peter Zijlstra
2014-07-14 11:55 ` Paolo Bonzini
2014-07-14 12:09 ` Peter Zijlstra
2014-07-14 12:40 ` Paolo Bonzini
2014-07-14 12:48 ` Peter Zijlstra
2014-07-14 13:47 ` Paolo Bonzini
2014-07-14 13:36 ` Liang, Kan
2014-07-14 13:40 ` Paolo Bonzini
2014-07-14 13:44 ` Liang, Kan
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=20140714105341.GT9918@twins.programming.kicks-ass.net \
--to=peterz@infradead.org \
--cc=andi@firstfloor.org \
--cc=kan.liang@intel.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.