On 5/17/2025 12:58 AM, Sean Christopherson wrote:
> On Fri, May 02, 2025, Manali Shukla wrote:
>> diff --git a/tools/testing/selftests/kvm/x86/kvm_buslock_test.c 
>> b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c
>> new file mode 100644
>> index 000000000000..9c081525ac2a
>> --- /dev/null
>> +++ b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c
>> @@ -0,0 +1,135 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2024 Advanced Micro Devices, Inc.
>> + */
>> +
>> +#include "test_util.h"
>> +#include "kvm_util.h"
>> +#include "processor.h"
>> +#include "svm_util.h"
>> +#include "vmx.h"
>> +
>> +#define NR_ITERATIONS 100
>> +#define L2_GUEST_STACK_SIZE 64
>> +
>> +#pragma GCC diagnostic push
>> +#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
> 
> Eww.
> 
>> +
>> +struct buslock_test {
>> +    unsigned char pad[PAGE_SIZE - 2];
>> +    atomic_long_t val;
>> +} __packed;
> 
> You don't need an entire page to generate a bus lock, two cache lines will do
> nicely.  And there's certain no need for __packed.
> 
>> +struct buslock_test test __aligned(PAGE_SIZE);
>> +
>> +static __always_inline void buslock_atomic_add(int i, atomic_long_t *v)
>> +{
>> +    asm volatile(LOCK_PREFIX "addl %1,%0"
>> +                 : "+m" (v->counter)
>> +                 : "ir" (i) : "memory");
>> +}
> 
> If only there were utilities for atomics...
> 
>> +static void buslock_add(void)
> 
> guest_generate_buslocks()
> 
>> +{
>> +    /*
>> +     * Increment a page unaligned variable atomically.
>> +     * This should generate a bus lock exit.
> 
> Not should, will.
> 
>> +     */
>> +    for (int i = 0; i < NR_ITERATIONS; i++)
>> +            buslock_atomic_add(2, &test.val);
> 
> Don't do weird and completely arbitrary things like adding '2' instead of '1',
> it makes readers look for intent and purpose that doesn't exist.
> 

Got it. Thanks for the feedback.

>> +}
> 
> ...
> 
>> +int main(int argc, char *argv[])
>> +{
>> +    struct kvm_vcpu *vcpu;
>> +    struct kvm_run *run;
>> +    struct kvm_vm *vm;
>> +    vm_vaddr_t nested_test_data_gva;
>> +
>> +    TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM) || 
>> kvm_cpu_has(X86_FEATURE_VMX));
> 
> There's no reason to make nested support a hard dependency, it's just as easy 
> to
> make it conditional.
> 
>> +    TEST_REQUIRE(kvm_has_cap(KVM_CAP_X86_BUS_LOCK_EXIT));
>> +
>> +    vm = vm_create(1);
>> +    vm_enable_cap(vm, KVM_CAP_X86_BUS_LOCK_EXIT, 
>> KVM_BUS_LOCK_DETECTION_EXIT);
>> +    vcpu = vm_vcpu_add(vm, 0, guest_code);
>> +
>> +    if (kvm_cpu_has(X86_FEATURE_SVM))
>> +            vcpu_alloc_svm(vm, &nested_test_data_gva);
>> +    else
>> +            vcpu_alloc_vmx(vm, &nested_test_data_gva);
>> +
>> +    vcpu_args_set(vcpu, 1, nested_test_data_gva);
>> +
>> +    run = vcpu->run;
>> +
>> +    for (;;) {
>> +            struct ucall uc;
>> +
>> +            vcpu_run(vcpu);
>> +
>> +            if (run->exit_reason == KVM_EXIT_IO) {
>> +                    switch (get_ucall(vcpu, &uc)) {
>> +                    case UCALL_ABORT:
>> +                            REPORT_GUEST_ASSERT(uc);
>> +                            /* NOT REACHED */
>> +                    case UCALL_SYNC:
>> +                            continue;
>> +                    case UCALL_DONE:
>> +                            goto done;
>> +                    default:
>> +                            TEST_FAIL("Unknown ucall 0x%lx.", uc.cmd);
>> +                    }
>> +            }
>> +
>> +            TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_X86_BUS_LOCK);
>> +    }
> 
> *sigh*
> 
> This doesn't actually ****VERIFY**** that the expected number of bus lock 
> exits
> were generated.  KVM could literally do nothing and the test will pass.  E.g. 
> the
> test passes if I do this:
> 
> diff --git a/tools/testing/selftests/kvm/x86/kvm_buslock_test.c 
> b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c
> index 9c081525ac2a..aa65d6be0f13 100644
> --- a/tools/testing/selftests/kvm/x86/kvm_buslock_test.c
> +++ b/tools/testing/selftests/kvm/x86/kvm_buslock_test.c
> @@ -93,10 +93,10 @@ int main(int argc, char *argv[])
>         vm_vaddr_t nested_test_data_gva;
>  
>         TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM) || 
> kvm_cpu_has(X86_FEATURE_VMX));
> -       TEST_REQUIRE(kvm_has_cap(KVM_CAP_X86_BUS_LOCK_EXIT));
> +//     TEST_REQUIRE(kvm_has_cap(KVM_CAP_X86_BUS_LOCK_EXIT));
>  
>         vm = vm_create(1);
> -       vm_enable_cap(vm, KVM_CAP_X86_BUS_LOCK_EXIT, 
> KVM_BUS_LOCK_DETECTION_EXIT);
> +//     vm_enable_cap(vm, KVM_CAP_X86_BUS_LOCK_EXIT, 
> KVM_BUS_LOCK_DETECTION_EXIT);
>         vcpu = vm_vcpu_add(vm, 0, guest_code);
>  
>         if (kvm_cpu_has(X86_FEATURE_SVM))
> --
> 
> The test would also fail to detect if KVM completely skipped the instruction.
> 
> This is not rocket science.  If you can't make your test fail by introducing 
> bugs
> in what you're testing, then your test is worthless.
> 
> No need for a v6, I'm going to do surgery when I apply, this series has 
> dragged
> on for far too long.

Understood. I agree the test should catch such failures — I’ll take that into 
account in future work.
Thanks for the review and for pushing this forward.

-Manali


Reply via email to