Re: [PATCH v1 5/5] selftests: KVM: SVM: Add Idle HLT intercept test

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

 



Hi Sean,

Thank you for reviewing my patches.

On 3/7/2024 11:52 PM, Sean Christopherson wrote:
> On Thu, Mar 07, 2024, Manali Shukla wrote:
>> From: Manali Shukla <Manali.Shukla@xxxxxxx>
>>
>> The Execution of the HLT instruction results in a VMEXIT. Hypervisor
>> observes pending V_INTR and V_NMI events just after the VMEXIT
>> generated by the HLT for the vCPU and causes VM entry to service the
>> pending events.  The Idle HLT intercept feature avoids the wasteful
>> VMEXIT during the halt if there are pending V_INTR and V_NMI events
>> for the vCPU.
>>
>> The selftest for the Idle HLT intercept instruments the above-mentioned
>> scenario.
>>
>> Signed-off-by: Manali Shukla <Manali.Shukla@xxxxxxx>
>> ---
>>  tools/testing/selftests/kvm/Makefile          |   1 +
>>  .../selftests/kvm/x86_64/svm_idlehlt_test.c   | 118 ++++++++++++++++++
>>  2 files changed, 119 insertions(+)
>>  create mode 100644 tools/testing/selftests/kvm/x86_64/svm_idlehlt_test.c
>>
>> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
>> index 492e937fab00..7ad03dc4f35e 100644
>> --- a/tools/testing/selftests/kvm/Makefile
>> +++ b/tools/testing/selftests/kvm/Makefile
>> @@ -89,6 +89,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/smaller_maxphyaddr_emulation_test
>>  TEST_GEN_PROGS_x86_64 += x86_64/smm_test
>>  TEST_GEN_PROGS_x86_64 += x86_64/state_test
>>  TEST_GEN_PROGS_x86_64 += x86_64/vmx_preemption_timer_test
>> +TEST_GEN_PROGS_x86_64 += x86_64/svm_idlehlt_test
> 
> Uber nit, maybe svm_idle_hlt_test?  I find "idlehlt" hard to parse.
Sure I will change it to svm_idle_hlt_test.

> 
>>  TEST_GEN_PROGS_x86_64 += x86_64/svm_vmcall_test
>>  TEST_GEN_PROGS_x86_64 += x86_64/svm_int_ctl_test
>>  TEST_GEN_PROGS_x86_64 += x86_64/svm_nested_shutdown_test
>> diff --git a/tools/testing/selftests/kvm/x86_64/svm_idlehlt_test.c b/tools/testing/selftests/kvm/x86_64/svm_idlehlt_test.c
>> new file mode 100644
>> index 000000000000..1564511799d4
>> --- /dev/null
>> +++ b/tools/testing/selftests/kvm/x86_64/svm_idlehlt_test.c
>> @@ -0,0 +1,118 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + *  svm_idlehalt_test
>> + *
> 
> Please omit this, file comments that state the name of the test inevitably
> become stale (see above).

Sure. I will remove it.
> 
>> + *  Copyright (C) 2024 Advanced Micro Devices, Inc.
>> + *
>> + *  For licencing details see kernel-base/COPYING
> 
> This seems gratuitous, doesn't the SPDX stuff take care this?

Agreed, I will remove this.

> 
>> + *
>> + *  Author:
>> + *  Manali Shukla  <manali.shukla@xxxxxxx>
>> + */
>> +#include "kvm_util.h"
>> +#include "svm_util.h"
>> +#include "processor.h"
>> +#include "test_util.h"
>> +#include "apic.h"
>> +
>> +#define VINTR_VECTOR     0x30
>> +#define NUM_ITERATIONS 100000
> 
> What's the runtime?  If it's less than a second, then whatever, but if it's at
> all longer than that, then I'd prefer to use a lower default and make this user-
> configurable.

It takes ~34s to run this test. 
> 
>> +/*
>> + * Incremented in the VINTR handler. Provides evidence to the sender that the
>> + * VINR is arrived at the destination.
> 
> Evidence is useless if there's no detective looking for it.  Yeah, it gets
> printed out in the end, but in reality, no one is going to look at that.
> 
> Rather than read this from the host, just make it a non-volatile bool and assert
> that it set after every 
> 

Sure. I can do that.

>> + */
>> +static volatile uint64_t vintr_rcvd;
>> +
>> +void verify_apic_base_addr(void)
>> +{
>> +	uint64_t msr = rdmsr(MSR_IA32_APICBASE);
>> +	uint64_t base = GET_APIC_BASE(msr);
>> +
>> +	GUEST_ASSERT(base == APIC_DEFAULT_GPA);
>> +}
>> +
>> +/*
>> + * The halting guest code instruments the scenario where there is a V_INTR pending
>> + * event available while hlt instruction is executed. The HLT VM Exit doesn't
>> + * occur in above-mentioned scenario if the Idle HLT intercept feature is enabled.
>> + */
>> +
>> +static void halter_guest_code(void)
> 
> Just "guest_code()".  Yeah, it's a weird generic name, but at this point it's so
> ubiquitous that it's analogous to main(), i.e. readers know that guest_code() is
> the entry point.  And deviating from that suggests that there is a second vCPU
> running _other_ guest code (otherwise, why differentiate?), which isn't the case.
> 

Sure.

>> +{
>> +	uint32_t icr_val;
>> +	int i;
>> +
>> +	verify_apic_base_addr();
> 
> Why?  The test will fail if the APIC is borked, this is just unnecessary noise
> that distracts readers.
> 
> Sure. I will remove it in V2.

>> +	xapic_enable();
>> +
>> +	icr_val = (APIC_DEST_SELF | APIC_INT_ASSERT | VINTR_VECTOR);
>> +
>> +	for (i = 0; i < NUM_ITERATIONS; i++) {
>> +		xapic_write_reg(APIC_ICR, icr_val);
>> +		asm volatile("sti; hlt; cli");
> 
> Please add safe_halt() and cli() helpers in processor.h.  And then do:
> 
> 		cli();
> 		xapic_write_reg(APIC_ICR, icr_val);
> 		safe_halt();
> 
> to guarantee that interrupts are disabled when the IPI is sent.  And as above,
> 
> 		GUEST_ASSERT(READ_ONCE(irq_received));
> 		WRITE_ONCE(irq_received, false);
> 

Sure.
>> +	}
>> +	GUEST_DONE();
>> +}
>> +
>> +static void guest_vintr_handler(struct ex_regs *regs)
>> +{
>> +	vintr_rcvd++;
>> +	xapic_write_reg(APIC_EOI, 0x30);
> 
> EOI is typically written with '0', not the vector, because the legacy EOI register
> clears the highest ISR vector, not whatever is specified.  IIRC, one of the Intel
> or AMD specs even says to use '0'.
> 
> AMD's Specific EOI register does allow targeting a specific vector, but that's
> not what's being used here.

Sure.
> 
>> +}
>> +
>> +int main(int argc, char *argv[])
>> +{
>> +	struct kvm_vm *vm;
>> +	struct kvm_vcpu *vcpu;
>> +	struct ucall uc;
>> +	uint64_t  halt_exits, vintr_exits;
>> +	uint64_t *pvintr_rcvd;
>> +
>> +	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));
> 
> No, this test doesn't require SVM, which is KVM advertising *nested* SVM.  This
> test does require idle-hlt support though...

Sure. I will add it in V2.

> 
>> +	/* Check the extension for binary stats */
>> +	TEST_REQUIRE(kvm_has_cap(KVM_CAP_BINARY_STATS_FD));
>> +
>> +	vm = vm_create_with_one_vcpu(&vcpu, halter_guest_code);
>> +
>> +	vm_init_descriptor_tables(vm);
>> +	vcpu_init_descriptor_tables(vcpu);
>> +	vm_install_exception_handler(vm, VINTR_VECTOR, guest_vintr_handler);
>> +	virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
>> +
>> +	vcpu_run(vcpu);
>> +	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
>> +
>> +	halt_exits = vcpu_get_stat(vcpu, "halt_exits");
> 
> Is there really no way to get binary stats without having to pass in strings?

I could see one of the test case is passing the strings to get binary stats of
vm. That is why I used strings to get binary stats of vcpu. I will try to find another
way to get the binary stats.

> 
>> +	vintr_exits = vcpu_get_stat(vcpu, "irq_window_exits");
>> +	pvintr_rcvd = (uint64_t *)addr_gva2hva(vm, (uint64_t)&vintr_rcvd);
>> +
>> +	switch (get_ucall(vcpu, &uc)) {
>> +	case UCALL_ABORT:
>> +		REPORT_GUEST_ASSERT(uc);
>> +		/* NOT REACHED */
> 
> Eh, just put a "break;" here and drop the comment.
> 
Sure.

>> +	case UCALL_DONE:
>> +		goto done;
> 
> break;
> 
>> +	default:
>> +		TEST_FAIL("Unknown ucall 0x%lx.", uc.cmd);
>> +	}
>> +
>> +done:
>> +	TEST_ASSERT(halt_exits == 0,
> 
> So in all honesty, this isn't a very interesting test.  It's more of a CPU test
> than a KVM test.  I do think it's worth adding, because why not.
> 
> But I'd also like to see a testcase for KVM_X86_DISABLE_EXITS_HLT.  It would be
> a generic test, i.e. not specific to idle-hlt since there is no dependency and
> the test shouldn't care.  I _think_ it would be fairly straightforward: create
> a VM without an in-kernel APIC (so that HLT exits to userspace), disable HLT
> interception, send a signal from a different task after some delay, and execute
> HLT in the guest.  Then verify the vCPU exited because of -EINTR and not HLT.

I will create this test.
> 
>> +		    "Test Failed:\n"
>> +		    "Guest executed VINTR followed by halts: %d times\n"
>> +		    "The guest exited due to halt: %ld times and number\n"
>> +		    "of vintr exits: %ld and vintr got re-injected: %ld times\n",
>> +		    NUM_ITERATIONS, halt_exits, vintr_exits, *pvintr_rcvd);
> 
> I appreciate the effort to provide more info, but this is way too noisy.  If
> anything, print gory details in a pr_debug() *before* the assert (see below),
> and then simply do:
> 
> 	TEST_ASSERT_EQ(halt_exits, 0);
> 
Sure.

>> +	fprintf(stderr,
>> +		"Test Successful:\n"
>> +		"Guest executed VINTR followed by halts: %d times\n"
>> +		"The guest exited due to halt: %ld times and number\n"
>> +		"of vintr exits: %ld and vintr got re-injected: %ld times\n",
>> +		NUM_ITERATIONS, halt_exits, vintr_exits, *pvintr_rcvd);
> 
> And this should be pr_debug(), because no human is going to look at this except
> when the test isn't working correctly.

I will change it to pr_debug() in V2.
> 
>> +
>> +	kvm_vm_free(vm);
>> +	return 0;
>> +}
>> -- 
>> 2.34.1
>> /pvintr_rcvd
> 





[Index of Archives]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Share Photos]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux