diff mbox series

KVM: selftests: Skip tests that require EPT when it is not available

Message ID 20220926171457.532542-1-dmatlack@google.com (mailing list archive)
State New, archived
Headers show
Series KVM: selftests: Skip tests that require EPT when it is not available | expand

Commit Message

David Matlack Sept. 26, 2022, 5:14 p.m. UTC
Skip selftests that require EPT support in the VM when it is not
available. For example, if running on a machine where kvm_intel.ept=N
since KVM does not offer EPT support to guests if EPT is not supported
on the host.

This commit causes vmx_dirty_log_test to be skipped instead of failing
on hosts where kvm_intel.ept=N.

Signed-off-by: David Matlack <dmatlack@google.com>
---
 .../selftests/kvm/include/x86_64/vmx.h        |  1 +
 tools/testing/selftests/kvm/lib/x86_64/vmx.c  | 20 +++++++++++++++++++
 2 files changed, 21 insertions(+)


base-commit: 372d07084593dc7a399bf9bee815711b1fb1bcf2
prerequisite-patch-id: 2e3661ba8856c29b769499bac525b6943d9284b8
prerequisite-patch-id: 93031262de7b1f7fab1ad31ea5d6ef812c139179

Comments

Sean Christopherson Sept. 26, 2022, 9:45 p.m. UTC | #1
On Mon, Sep 26, 2022, David Matlack wrote:
> +bool kvm_vm_has_ept(struct kvm_vm *vm)
> +{
> +	struct kvm_vcpu *vcpu;
> +	uint64_t ctrl;
> +
> +	vcpu = list_first_entry(&vm->vcpus, struct kvm_vcpu, list);
> +	TEST_ASSERT(vcpu, "Cannot determine EPT support without vCPUs.\n");

KVM_GET_MSRS is supported on /dev/kvm for feature MSRs, and is available for
selftests via kvm_get_feature_msr().

> +
> +	ctrl = vcpu_get_msr(vcpu, MSR_IA32_VMX_TRUE_PROCBASED_CTLS) >> 32;
> +	if (!(ctrl & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS))
> +		return false;
> +
> +	ctrl = vcpu_get_msr(vcpu, MSR_IA32_VMX_PROCBASED_CTLS2) >> 32;
> +	return ctrl & SECONDARY_EXEC_ENABLE_EPT;
> +}
> +
>  void prepare_eptp(struct vmx_pages *vmx, struct kvm_vm *vm,
>  		  uint32_t eptp_memslot)
>  {
> +	TEST_REQUIRE(kvm_vm_has_ept(vm));

I would much rather this be an assert, i.e. force the test to do TEST_REQUIRE(),
even if that means duplicate code.  One of the roles of TEST_REQUIRE() is to
document test requirements.
David Matlack Sept. 26, 2022, 10:18 p.m. UTC | #2
On Mon, Sep 26, 2022 at 2:45 PM Sean Christopherson <seanjc@google.com> wrote:
>
> On Mon, Sep 26, 2022, David Matlack wrote:
> > +bool kvm_vm_has_ept(struct kvm_vm *vm)
> > +{
> > +     struct kvm_vcpu *vcpu;
> > +     uint64_t ctrl;
> > +
> > +     vcpu = list_first_entry(&vm->vcpus, struct kvm_vcpu, list);
> > +     TEST_ASSERT(vcpu, "Cannot determine EPT support without vCPUs.\n");
>
> KVM_GET_MSRS is supported on /dev/kvm for feature MSRs, and is available for
> selftests via kvm_get_feature_msr().

Ack.

>
> > +
> > +     ctrl = vcpu_get_msr(vcpu, MSR_IA32_VMX_TRUE_PROCBASED_CTLS) >> 32;
> > +     if (!(ctrl & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS))
> > +             return false;
> > +
> > +     ctrl = vcpu_get_msr(vcpu, MSR_IA32_VMX_PROCBASED_CTLS2) >> 32;
> > +     return ctrl & SECONDARY_EXEC_ENABLE_EPT;
> > +}
> > +
> >  void prepare_eptp(struct vmx_pages *vmx, struct kvm_vm *vm,
> >                 uint32_t eptp_memslot)
> >  {
> > +     TEST_REQUIRE(kvm_vm_has_ept(vm));
>
> I would much rather this be an assert, i.e. force the test to do TEST_REQUIRE(),
> even if that means duplicate code.  One of the roles of TEST_REQUIRE() is to
> document test requirements.

This gets difficult when you consider dirty_log_perf_test. Users can
use dirty_log_perf_test in nested mode by passing "-n". But
dirty_log_perf_test is an architecture-neutral test, so adding
TEST_REQUIRE() there would require an ifdef, and then gets even more
complicated if we add support for AMD or nested on non-x86
architectures.

One option is to put the TEST_REQUIRE() in the x86-specific
perf_test_setup_ept(), but that is only one level above
prepare_eptp(), so not exactly any better at documenting the
requirement.

Another option is to do nothing and let the test fail if running on
hosts without EPT. I don't like this solution though, because that
means developers and automation would need custom logic to skip
running dirty_log_perf_test with -n depending on the state of the
host. (For context: I typically run all selftests and kvm-unit-tests
against kvm with ept=Y and ept=N.)

At least for vmx_dirty_log_test, the TEST_REQUIRE() could be put in main().
Sean Christopherson Sept. 26, 2022, 10:28 p.m. UTC | #3
On Mon, Sep 26, 2022, David Matlack wrote:
> On Mon, Sep 26, 2022 at 2:45 PM Sean Christopherson <seanjc@google.com> wrote:
> > I would much rather this be an assert, i.e. force the test to do TEST_REQUIRE(),
> > even if that means duplicate code.  One of the roles of TEST_REQUIRE() is to
> > document test requirements.
> 
> This gets difficult when you consider dirty_log_perf_test. Users can
> use dirty_log_perf_test in nested mode by passing "-n". But
> dirty_log_perf_test is an architecture-neutral test, so adding
> TEST_REQUIRE() there would require an ifdef, and then gets even more
> complicated if we add support for AMD or nested on non-x86
> architectures.

But you're going to have that problem regardless, e.g. perf_test_setup_nested()
already has

	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX));

so why not also have the EPT requirement there?  IMO, it's far less confusing if
the hard requirements are collected together, even if that one location isn't ideal.

If someone wants to improve the memstress framework, a hook can be added for probing
nested requirements.  In other words, IMO this is a shortcoming of the memstress code,
not a valid reason to shove the requirement down in common code.

> Another option is to do nothing and let the test fail if running on
> hosts without EPT. I don't like this solution though

Neither do I.
David Matlack Sept. 26, 2022, 10:40 p.m. UTC | #4
On Mon, Sep 26, 2022 at 3:29 PM Sean Christopherson <seanjc@google.com> wrote:
>
> On Mon, Sep 26, 2022, David Matlack wrote:
> > On Mon, Sep 26, 2022 at 2:45 PM Sean Christopherson <seanjc@google.com> wrote:
> > > I would much rather this be an assert, i.e. force the test to do TEST_REQUIRE(),
> > > even if that means duplicate code.  One of the roles of TEST_REQUIRE() is to
> > > document test requirements.
> >
> > This gets difficult when you consider dirty_log_perf_test. Users can
> > use dirty_log_perf_test in nested mode by passing "-n". But
> > dirty_log_perf_test is an architecture-neutral test, so adding
> > TEST_REQUIRE() there would require an ifdef, and then gets even more
> > complicated if we add support for AMD or nested on non-x86
> > architectures.
>
> But you're going to have that problem regardless, e.g. perf_test_setup_nested()
> already has
>
>         TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX));
>
> so why not also have the EPT requirement there?  IMO, it's far less confusing if
> the hard requirements are collected together, even if that one location isn't ideal.

Ack, can do.

>
> If someone wants to improve the memstress framework, a hook can be added for probing
> nested requirements.  In other words, IMO this is a shortcoming of the memstress code,
> not a valid reason to shove the requirement down in common code.

Sorry I forgot to ask this in my previous reply... Why do you prefer
to decouple test requirements from the test setup code? There is a
maintenance burden to such an approach, so I want to understand the
benefit. e.g. I forsee myself having to send patches in the future to
add TEST_REQUIRE(kvm_cpu_has_ept()), because someone added a new VMX
test and forgot to test with ept=N.
Sean Christopherson Sept. 26, 2022, 11:57 p.m. UTC | #5
On Mon, Sep 26, 2022, David Matlack wrote:
> On Mon, Sep 26, 2022 at 3:29 PM Sean Christopherson <seanjc@google.com> wrote:
> > If someone wants to improve the memstress framework, a hook can be added for probing
> > nested requirements.  In other words, IMO this is a shortcoming of the memstress code,
> > not a valid reason to shove the requirement down in common code.
> 
> Sorry I forgot to ask this in my previous reply... Why do you prefer
> to decouple test requirements from the test setup code? There is a
> maintenance burden to such an approach, so I want to understand the
> benefit. e.g. I forsee myself having to send patches in the future to
> add TEST_REQUIRE(kvm_cpu_has_ept()), because someone added a new VMX
> test and forgot to test with ept=N.

I don't necessarily prefer decoupling, what I really dislike is having the TEST_REQUIRE()
buried deep down, because it's not clear from the reader whether or not TDP/EPT
is truly required, and if it is a hard requirement, it's not easily visible to
the reader.  The print_skip() output helps, but that obviously requires actually
trying to run the test.

E.g. I wouldn't object as much if perf_test_setup_nested() looked like this:

  void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu *vcpus[])
  {
	struct vmx_pages *vmx, *vmx0 = NULL;
	struct kvm_regs regs;
	vm_vaddr_t vmx_gva;
	int vcpu_id;

	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX));
	TEST_REQUEST(perf_test_setup_ept(vm));

	for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) {
		vmx = vcpu_alloc_vmx(vm, &vmx_gva);

		...
	}
  }

I'd still object to some extent, because that obfuscates that the requirement is
that KVM supports nested EPT, e.g. one might wonder why EPT setup can fail, and
because there's really no need for prepare_eptp() to exist.  If/when "struct vmx_page"
is a thing[*], then prepare_eptp() goes away and becomes

	vm_alloc_vmx_page(<pointer to a struct vmx_page>);

and so there's not even a real place to shove the TEST_REQUIRE().

And I 100% agree there's a maintenance burden, but it's fairly small and it's
only paid once per test, whereas making it even the tiniest bit difficult to
understand a test's requirements incurs some amount of cost every time someone
reads the code.

E.g. the memstress code ends up looking something like this:

  void perf_test_setup_ept(struct vmx_page *eptp, struct kvm_vm *vm)
  {
	uint64_t start, end;

	vm_alloc_vmx_page(eptp)

	/*
	 * Identity map the first 4G and the test region with 1G pages so that
	 * KVM can shadow the EPT12 with the maximum huge page size supported
	 * by the backing source.
	 */
	nested_identity_map_1g(eptp, vm, 0, 0x100000000ULL);

	start = align_down(perf_test_args.gpa, PG_SIZE_1G);
	end = align_up(perf_test_args.gpa + perf_test_args.size, PG_SIZE_1G);
	nested_identity_map_1g(eptp, vm, start, end - start);
  }

  void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu *vcpus[])
  {
	struct vmx_pages *vmx;
	struct vmx_page eptp;
	struct kvm_regs regs;
	vm_vaddr_t vmx_gva;
	int vcpu_id;

	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX));
	TEST_REQUEST(kvm_cpu_has_ept());

	perf_test_setup_ept(eptp, vm);

	for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) {
		vmx = vcpu_alloc_vmx(vm, &vmx_gva);

		memcpy(vmx->eptp, &eptp, sizeof(eptp));

		/*
		 * Override the vCPU to run perf_test_l1_guest_code() which will
		 * bounce it into L2 before calling perf_test_guest_code().
		 */
		vcpu_regs_get(vcpus[vcpu_id], &regs);
		regs.rip = (unsigned long) perf_test_l1_guest_code;
		vcpu_regs_set(vcpus[vcpu_id], &regs);
		vcpu_args_set(vcpus[vcpu_id], 2, vmx_gva, vcpu_id);
	}
  }

and at that point, IMO adding a helper to assert/require EPT is contrived and not
necessarily a net positive.

[*] https://lore.kernel.org/all/YwznLAqRb2i4lHiH@google.com
Paolo Bonzini Sept. 27, 2022, 11:58 a.m. UTC | #6
On 9/26/22 19:14, David Matlack wrote:
> Skip selftests that require EPT support in the VM when it is not
> available. For example, if running on a machine where kvm_intel.ept=N
> since KVM does not offer EPT support to guests if EPT is not supported
> on the host.
> 
> This commit causes vmx_dirty_log_test to be skipped instead of failing
> on hosts where kvm_intel.ept=N.
> 
> Signed-off-by: David Matlack <dmatlack@google.com>
> ---
>   .../selftests/kvm/include/x86_64/vmx.h        |  1 +
>   tools/testing/selftests/kvm/lib/x86_64/vmx.c  | 20 +++++++++++++++++++
>   2 files changed, 21 insertions(+)
> 
> diff --git a/tools/testing/selftests/kvm/include/x86_64/vmx.h b/tools/testing/selftests/kvm/include/x86_64/vmx.h
> index 99fa1410964c..790c6d1ecb34 100644
> --- a/tools/testing/selftests/kvm/include/x86_64/vmx.h
> +++ b/tools/testing/selftests/kvm/include/x86_64/vmx.h
> @@ -617,6 +617,7 @@ void nested_map_memslot(struct vmx_pages *vmx, struct kvm_vm *vm,
>   			uint32_t memslot);
>   void nested_identity_map_1g(struct vmx_pages *vmx, struct kvm_vm *vm,
>   			    uint64_t addr, uint64_t size);
> +bool kvm_vm_has_ept(struct kvm_vm *vm);
>   void prepare_eptp(struct vmx_pages *vmx, struct kvm_vm *vm,
>   		  uint32_t eptp_memslot);
>   void prepare_virtualize_apic_accesses(struct vmx_pages *vmx, struct kvm_vm *vm);
> diff --git a/tools/testing/selftests/kvm/lib/x86_64/vmx.c b/tools/testing/selftests/kvm/lib/x86_64/vmx.c
> index 80a568c439b8..d21049c38fc5 100644
> --- a/tools/testing/selftests/kvm/lib/x86_64/vmx.c
> +++ b/tools/testing/selftests/kvm/lib/x86_64/vmx.c
> @@ -5,6 +5,8 @@
>    * Copyright (C) 2018, Google LLC.
>    */
>   
> +#include <asm/msr-index.h>
> +
>   #include "test_util.h"
>   #include "kvm_util.h"
>   #include "processor.h"
> @@ -542,9 +544,27 @@ void nested_identity_map_1g(struct vmx_pages *vmx, struct kvm_vm *vm,
>   	__nested_map(vmx, vm, addr, addr, size, PG_LEVEL_1G);
>   }
>   
> +bool kvm_vm_has_ept(struct kvm_vm *vm)
> +{
> +	struct kvm_vcpu *vcpu;
> +	uint64_t ctrl;
> +
> +	vcpu = list_first_entry(&vm->vcpus, struct kvm_vcpu, list);
> +	TEST_ASSERT(vcpu, "Cannot determine EPT support without vCPUs.\n");
> +
> +	ctrl = vcpu_get_msr(vcpu, MSR_IA32_VMX_TRUE_PROCBASED_CTLS) >> 32;
> +	if (!(ctrl & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS))
> +		return false;
> +
> +	ctrl = vcpu_get_msr(vcpu, MSR_IA32_VMX_PROCBASED_CTLS2) >> 32;
> +	return ctrl & SECONDARY_EXEC_ENABLE_EPT;
> +}
> +
>   void prepare_eptp(struct vmx_pages *vmx, struct kvm_vm *vm,
>   		  uint32_t eptp_memslot)
>   {
> +	TEST_REQUIRE(kvm_vm_has_ept(vm));
> +
>   	vmx->eptp = (void *)vm_vaddr_alloc_page(vm);
>   	vmx->eptp_hva = addr_gva2hva(vm, (uintptr_t)vmx->eptp);
>   	vmx->eptp_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->eptp);
> 
> base-commit: 372d07084593dc7a399bf9bee815711b1fb1bcf2
> prerequisite-patch-id: 2e3661ba8856c29b769499bac525b6943d9284b8
> prerequisite-patch-id: 93031262de7b1f7fab1ad31ea5d6ef812c139179

Queued for 6.0, thanks.

Paolo
David Matlack Sept. 27, 2022, 4:54 p.m. UTC | #7
On Tue, Sep 27, 2022 at 4:58 AM Paolo Bonzini <pbonzini@redhat.com> wrote:
>
> On 9/26/22 19:14, David Matlack wrote:
> > Skip selftests that require EPT support in the VM when it is not
> > available. For example, if running on a machine where kvm_intel.ept=N
> > since KVM does not offer EPT support to guests if EPT is not supported
> > on the host.
> >
> > This commit causes vmx_dirty_log_test to be skipped instead of failing
> > on hosts where kvm_intel.ept=N.
> >
> > Signed-off-by: David Matlack <dmatlack@google.com>
>
> Queued for 6.0, thanks.

I sent a v2 based on the feedback from Sean. Please grab that one
instead. Thanks.
diff mbox series

Patch

diff --git a/tools/testing/selftests/kvm/include/x86_64/vmx.h b/tools/testing/selftests/kvm/include/x86_64/vmx.h
index 99fa1410964c..790c6d1ecb34 100644
--- a/tools/testing/selftests/kvm/include/x86_64/vmx.h
+++ b/tools/testing/selftests/kvm/include/x86_64/vmx.h
@@ -617,6 +617,7 @@  void nested_map_memslot(struct vmx_pages *vmx, struct kvm_vm *vm,
 			uint32_t memslot);
 void nested_identity_map_1g(struct vmx_pages *vmx, struct kvm_vm *vm,
 			    uint64_t addr, uint64_t size);
+bool kvm_vm_has_ept(struct kvm_vm *vm);
 void prepare_eptp(struct vmx_pages *vmx, struct kvm_vm *vm,
 		  uint32_t eptp_memslot);
 void prepare_virtualize_apic_accesses(struct vmx_pages *vmx, struct kvm_vm *vm);
diff --git a/tools/testing/selftests/kvm/lib/x86_64/vmx.c b/tools/testing/selftests/kvm/lib/x86_64/vmx.c
index 80a568c439b8..d21049c38fc5 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/vmx.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/vmx.c
@@ -5,6 +5,8 @@ 
  * Copyright (C) 2018, Google LLC.
  */
 
+#include <asm/msr-index.h>
+
 #include "test_util.h"
 #include "kvm_util.h"
 #include "processor.h"
@@ -542,9 +544,27 @@  void nested_identity_map_1g(struct vmx_pages *vmx, struct kvm_vm *vm,
 	__nested_map(vmx, vm, addr, addr, size, PG_LEVEL_1G);
 }
 
+bool kvm_vm_has_ept(struct kvm_vm *vm)
+{
+	struct kvm_vcpu *vcpu;
+	uint64_t ctrl;
+
+	vcpu = list_first_entry(&vm->vcpus, struct kvm_vcpu, list);
+	TEST_ASSERT(vcpu, "Cannot determine EPT support without vCPUs.\n");
+
+	ctrl = vcpu_get_msr(vcpu, MSR_IA32_VMX_TRUE_PROCBASED_CTLS) >> 32;
+	if (!(ctrl & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS))
+		return false;
+
+	ctrl = vcpu_get_msr(vcpu, MSR_IA32_VMX_PROCBASED_CTLS2) >> 32;
+	return ctrl & SECONDARY_EXEC_ENABLE_EPT;
+}
+
 void prepare_eptp(struct vmx_pages *vmx, struct kvm_vm *vm,
 		  uint32_t eptp_memslot)
 {
+	TEST_REQUIRE(kvm_vm_has_ept(vm));
+
 	vmx->eptp = (void *)vm_vaddr_alloc_page(vm);
 	vmx->eptp_hva = addr_gva2hva(vm, (uintptr_t)vmx->eptp);
 	vmx->eptp_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->eptp);