Re: [V3 PATCH 1/2] KVM: selftests: x86: Cache the cpu vendor type

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

 



On Thu, Dec 22, 2022, Vishal Annapurve wrote:
> Query cpuid once per guest selftest and store the cpu vendor type so that
> subsequent queries can reuse the cached cpu vendor type.
> 
> Signed-off-by: Vishal Annapurve <vannapurve@xxxxxxxxxx>
> ---
>  .../selftests/kvm/lib/x86_64/processor.c      | 33 ++++++++++++++++---
>  1 file changed, 28 insertions(+), 5 deletions(-)
> 
> diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
> index 097cef430774..1e93bb3cb058 100644
> --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
> +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
> @@ -20,6 +20,9 @@
>  
>  vm_vaddr_t exception_handlers;
>  
> +static bool is_cpu_vendor_intel;
> +static bool is_cpu_vendor_amd;
> +
>  static void regs_dump(FILE *stream, struct kvm_regs *regs, uint8_t indent)
>  {
>  	fprintf(stream, "%*srax: 0x%.16llx rbx: 0x%.16llx "
> @@ -1017,18 +1020,36 @@ void kvm_x86_state_cleanup(struct kvm_x86_state *state)
>  	free(state);
>  }
>  
> -static bool cpu_vendor_string_is(const char *vendor)
> +static void check_cpu_vendor(void)

I don't love the name, "check" makes me think the purpose of the helper is to
assert something.  Maybe cache_cpu_vendor()?  Though this might be a moot point
(more at the end).

>  {
> -	const uint32_t *chunk = (const uint32_t *)vendor;
> +	const char *intel_id = "GenuineIntel";
> +	const uint32_t *intel_id_chunks = (const uint32_t *)intel_id;
> +	const char *amd_id = "AuthenticAMD";
> +	const uint32_t *amd_id_chunks = (const uint32_t *)amd_id;
> +	static bool cpu_vendor_checked;
>  	uint32_t eax, ebx, ecx, edx;
>  
> +	if (cpu_vendor_checked)
> +		return;
> +
>  	cpuid(0, &eax, &ebx, &ecx, &edx);
> -	return (ebx == chunk[0] && edx == chunk[1] && ecx == chunk[2]);
> +
> +	if (ebx == intel_id_chunks[0] && edx == intel_id_chunks[1] &&
> +		ecx == intel_id_chunks[2])

Align indentation, should be:

	if (ebx == intel_id_chunks[0] && edx == intel_id_chunks[1] &&
	    ecx == intel_id_chunks[2])

> +		is_cpu_vendor_intel = true;
> +	else {
> +		if (ebx == amd_id_chunks[0] && edx == amd_id_chunks[1] &&
> +			ecx == amd_id_chunks[2])

Same here.

> +			is_cpu_vendor_amd = true;
> +	}

Though that's likely a moot point since manually checking the chunks (multiple
times!) is rather heinous.  Just store the CPUID output into an array and then
use strncmp() to check the vendor.  Performance isn't a priority for this code.

  static void cache_cpu_vendor(void)
  {
	uint32_t ign, vendor[3];
	static bool cached;

	if (cached)
		return;

	cpuid(0, &ign, &vendor[0], &vendor[2], &vendor[1]);

	is_cpu_vendor_intel = !strncmp((char *)vendor, "GenuineIntel", 12);
	is_cpu_vendor_amd = !strncmp((char *)vendor, "AuthenticAMD", 12);

	cached = true;
  }

That said, I like the v2 approach a lot more, we just need to better name the
host variables to make it very clear that the info being cached is the _host_
vendor, and then provide separate helpers that bypass caching (though I don't
think there would be any users?), again with better names.

The confidential VM use case, and really kvm_hypercall() in general, cares about
the host vendor, i.e. which hypercall instruction won't fault.  Actually, even
fix_hypercall_test is in the same boat.

I don't like auto-caching the guest info because unlike the host (assuming no
shenanigans in a nested setup), the guest vendor string can be changed.  I don't
think it's likely that we'll have a test that wants to muck with the vendor string
on the fly, but it's not impossible, e.g. fix_hypercall_test dances pretty close
to that.

The independent guest vs. host caching in this version is also very subtle, though
that can be solved with comments.

E.g. first make is_intel_cpu() and is_amd_cpu() wrappers to non-caching helpers
that use "this_cpu_..." naming to match other selftests code.  Then rename
is_intel_cpu() and is_amd_cpu() to is_{intel,amd}_host(), with a blurb in the
changelog calling out that fix_hypercall_test wants the host vendor and also passes
through the host CPUID vendor.  And then do the precomputation stuff like in v2.

E.g. for step #1

---
 .../selftests/kvm/include/x86_64/processor.h  | 22 +++++++++++++++++++
 .../selftests/kvm/lib/x86_64/processor.c      | 13 ++---------
 2 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
index b1a31de7108a..34523a876336 100644
--- a/tools/testing/selftests/kvm/include/x86_64/processor.h
+++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
@@ -554,6 +554,28 @@ static inline uint32_t this_cpu_model(void)
 	return x86_model(this_cpu_fms());
 }
 
+static inline bool this_cpu_vendor_string_is(const char *vendor)
+{
+	const uint32_t *chunk = (const uint32_t *)vendor;
+	uint32_t eax, ebx, ecx, edx;
+
+	cpuid(0, &eax, &ebx, &ecx, &edx);
+	return (ebx == chunk[0] && edx == chunk[1] && ecx == chunk[2]);
+}
+
+static inline bool this_cpu_is_intel(void)
+{
+	return this_cpu_vendor_string_is("GenuineIntel");
+}
+
+/*
+ * Exclude early K5 samples with a vendor string of "AMDisbetter!"
+ */
+static inline bool this_cpu_is_amd(void)
+{
+	return this_cpu_vendor_string_is("AuthenticAMD");
+}
+
 static inline uint32_t __this_cpu_has(uint32_t function, uint32_t index,
 				      uint8_t reg, uint8_t lo, uint8_t hi)
 {
diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
index c4d368d56cfe..fae1a8a81652 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
@@ -1006,18 +1006,9 @@ void kvm_x86_state_cleanup(struct kvm_x86_state *state)
 	free(state);
 }
 
-static bool cpu_vendor_string_is(const char *vendor)
-{
-	const uint32_t *chunk = (const uint32_t *)vendor;
-	uint32_t eax, ebx, ecx, edx;
-
-	cpuid(0, &eax, &ebx, &ecx, &edx);
-	return (ebx == chunk[0] && edx == chunk[1] && ecx == chunk[2]);
-}
-
 bool is_intel_cpu(void)
 {
-	return cpu_vendor_string_is("GenuineIntel");
+	return this_cpu_is_intel();
 }
 
 /*
@@ -1025,7 +1016,7 @@ bool is_intel_cpu(void)
  */
 bool is_amd_cpu(void)
 {
-	return cpu_vendor_string_is("AuthenticAMD");
+	return this_cpu_is_amd();
 }
 
 void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits)

base-commit: d70e740240a298d0ff54d26f48f2fb034e3cb172
-- 




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux