From: Reinette Chatre <reinette.chatre@intel.com>
To: Sean Christopherson <seanjc@google.com>
Cc: <isaku.yamahata@intel.com>, <pbonzini@redhat.com>,
<erdemaktas@google.com>, <vkuznets@redhat.com>,
<vannapurve@google.com>, <jmattson@google.com>,
<mlevitsk@redhat.com>, <xiaoyao.li@intel.com>,
<chao.gao@intel.com>, <rick.p.edgecombe@intel.com>,
<yuan.yao@intel.com>, <kvm@vger.kernel.org>,
<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH V8 2/2] KVM: selftests: Add test for configure of x86 APIC bus frequency
Date: Tue, 11 Jun 2024 14:34:58 -0700 [thread overview]
Message-ID: <fba4628f-9786-4e76-84cb-178508d90fd8@intel.com> (raw)
In-Reply-To: <ZmefjsFArRSnC71I@google.com>
Hi Sean,
On 6/10/24 5:51 PM, Sean Christopherson wrote:
> On Mon, Jun 10, 2024, Reinette Chatre wrote:
>> diff --git a/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c b/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c
>> new file mode 100644
>> index 000000000000..602cec91d8ee
>> --- /dev/null
>> +++ b/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c
>> @@ -0,0 +1,219 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2024 Intel Corporation
>> + *
>> + * Verify KVM correctly emulates the APIC bus frequency when the VMM configures
>> + * the frequency via KVM_CAP_X86_APIC_BUS_CYCLES_NS. Start the APIC timer by
>> + * programming TMICT (timer initial count) to the largest value possible (so
>> + * that the timer will not expire during the test). Then, after an arbitrary
>> + * amount of time has elapsed, verify TMCCT (timer current count) is within 1%
>> + * of the expected value based on the time elapsed, the APIC bus frequency, and
>> + * the programmed TDCR (timer divide configuration register).
>> + */
>> +
>> +#include "apic.h"
>> +#include "test_util.h"
>> +
>> +/*
>> + * Pick 25MHz for APIC bus frequency. Different enough from the default 1GHz.
>> + * User can override via command line.
>> + */
>> +unsigned long apic_hz = 25 * 1000 * 1000;
>
> static, and maybe a uint64_t to match the other stuff?
Sure. Also moved all other globals and functions back to static.
>
>> +/*
>> + * Possible TDCR values with matching divide count. Used to modify APIC
>> + * timer frequency.
>> + */
>> +struct {
>> + uint32_t tdcr;
>> + uint32_t divide_count;
>> +} tdcrs[] = {
>> + {0x0, 2},
>> + {0x1, 4},
>> + {0x2, 8},
>> + {0x3, 16},
>> + {0x8, 32},
>> + {0x9, 64},
>> + {0xa, 128},
>> + {0xb, 1},
>> +};
>> +
>> +void guest_verify(uint64_t tsc_cycles, uint32_t apic_cycles, uint32_t divide_count)
>
> uin64_t for apic_cycles? And maybe something like guest_check_apic_count(), to
> make it more obvious what is being verified? Actually, it should be quite easy
> to have the two flavors share the bulk of the code.
I now plan to drop this function and instead just open code the
checks in what has now become a shared function between xAPIC and x2APIC.
>
>> +{
>> + unsigned long tsc_hz = tsc_khz * 1000;
>> + uint64_t freq;
>> +
>> + GUEST_ASSERT(tsc_cycles > 0);
>
> Is this necessary? Won't the "freq < ..." check fail? I love me some paranoia,
> but this seems unnecessary.
Sure. After needing to field reports from static checkers not able to determine
that a denominator can never be zero I do tend to add these checks just to
pre-emptively placate them. I did run the code through a static checker after making
all planned changes and it had no complaints so it is now gone.
>
>> + freq = apic_cycles * divide_count * tsc_hz / tsc_cycles;
>> + /* Check if measured frequency is within 1% of configured frequency. */
>> + GUEST_ASSERT(freq < apic_hz * 101 / 100);
>> + GUEST_ASSERT(freq > apic_hz * 99 / 100);
>> +}
>> +
>> +void x2apic_guest_code(void)
>> +{
>> + uint32_t tmict, tmcct;
>> + uint64_t tsc0, tsc1;
>> + int i;
>> +
>> + x2apic_enable();
>> +
>> + /*
>> + * Setup one-shot timer. The vector does not matter because the
>> + * interrupt should not fire.
>> + */
>> + x2apic_write_reg(APIC_LVTT, APIC_LVT_TIMER_ONESHOT | APIC_LVT_MASKED);
>> +
>> + for (i = 0; i < ARRAY_SIZE(tdcrs); i++) {
>> + x2apic_write_reg(APIC_TDCR, tdcrs[i].tdcr);
>> +
>> + /* Set the largest value to not trigger the interrupt. */
>
> Nit, the goal isn't to avoid triggering the interrupt, e.g. the above masking
> takes care of that. The goal is to prevent the timer from expiring, because if
> the timer expires it stops counting and will trigger a false failure because the
> TSC doesn't stop counting.
>
> Honestly, I would just delete the comment. Same with the "busy wait for 100 msec"
> and "Read APIC timer and TSC" comments. They state the obvious. Loading the max
> TMICT is mildly interesting, but that's covered by the file-level comment.
>
>> + tmict = ~0;
>
> This really belongs outside of the loop, e.g.
>
> const uint32_t tmict = ~0u;
>
>> + x2apic_write_reg(APIC_TMICT, tmict);
>> +
>> + /* Busy wait for 100 msec. */
>
> Hmm, should this be configurable?
Will do.
>
>> + tsc0 = rdtsc();
>> + udelay(100000);
>> + /* Read APIC timer and TSC. */
>> + tmcct = x2apic_read_reg(APIC_TMCCT);
>> + tsc1 = rdtsc();
>> +
>> + /* Stop timer. */
>
> This comment is a bit more interesting, as readers might not know writing '0'
> stops the timer. But that's even more interesting is the ordering, e.g. it's
> not at all unreasonable to think that the timer should be stopped _before_ reading
> the current count. E.g. something like:
>
> /*
> * Stop the timer _after_ reading the current, final count, as
> * writing the initial counter also modifies the current count.
> */
>
>> + x2apic_write_reg(APIC_TMICT, 0);
>> +
>> + guest_verify(tsc1 - tsc0, tmict - tmcct, tdcrs[i].divide_count);
>> + }
>> +
>> + GUEST_DONE();
>> +}
>> +
>> +void xapic_guest_code(void)
>> +{
>> + uint32_t tmict, tmcct;
>> + uint64_t tsc0, tsc1;
>> + int i;
>> +
>> + xapic_enable();
>> +
>> + /*
>> + * Setup one-shot timer. The vector does not matter because the
>> + * interrupt should not fire.
>> + */
>> + xapic_write_reg(APIC_LVTT, APIC_LVT_TIMER_ONESHOT | APIC_LVT_MASKED);
>> +
>> + for (i = 0; i < ARRAY_SIZE(tdcrs); i++) {
>> + xapic_write_reg(APIC_TDCR, tdcrs[i].tdcr);
>> +
>> + /* Set the largest value to not trigger the interrupt. */
>> + tmict = ~0;
>> + xapic_write_reg(APIC_TMICT, tmict);
>> +
>> + /* Busy wait for 100 msec. */
>> + tsc0 = rdtsc();
>> + udelay(100000);
>> + /* Read APIC timer and TSC. */
>> + tmcct = xapic_read_reg(APIC_TMCCT);
>> + tsc1 = rdtsc();
>> +
>> + /* Stop timer. */
>> + xapic_write_reg(APIC_TMICT, 0);
>> +
>> + guest_verify(tsc1 - tsc0, tmict - tmcct, tdcrs[i].divide_count);
>
> That's some nice copy+paste :-)
>
> This test isn't writing ICR, so the whole 32-bit vs. 64-bit weirdness with xAPIC
> vs X2APIC is irrevelant. Two tiny helpers, a global flag, and you can avoid a
> pile of copy+paste, and the need to find a better name than guest_verify().
Will do. Thank you very much for your detailed and valuable feedback.
Reinette
prev parent reply other threads:[~2024-06-11 21:35 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-06-10 18:25 [PATCH V8 0/2] KVM: x86: Make bus clock frequency for vAPIC timer configurable Reinette Chatre
2024-06-10 18:25 ` [PATCH V8 1/2] KVM: selftests: Add x86_64 guest udelay() utility Reinette Chatre
2024-06-11 0:21 ` Sean Christopherson
2024-06-11 21:33 ` Reinette Chatre
2024-06-11 22:03 ` Sean Christopherson
2024-06-11 23:07 ` Reinette Chatre
2024-06-12 1:15 ` Sean Christopherson
2024-06-12 17:49 ` Reinette Chatre
2024-06-10 18:25 ` [PATCH V8 2/2] KVM: selftests: Add test for configure of x86 APIC bus frequency Reinette Chatre
2024-06-11 0:51 ` Sean Christopherson
2024-06-11 21:34 ` Reinette Chatre [this message]
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=fba4628f-9786-4e76-84cb-178508d90fd8@intel.com \
--to=reinette.chatre@intel.com \
--cc=chao.gao@intel.com \
--cc=erdemaktas@google.com \
--cc=isaku.yamahata@intel.com \
--cc=jmattson@google.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mlevitsk@redhat.com \
--cc=pbonzini@redhat.com \
--cc=rick.p.edgecombe@intel.com \
--cc=seanjc@google.com \
--cc=vannapurve@google.com \
--cc=vkuznets@redhat.com \
--cc=xiaoyao.li@intel.com \
--cc=yuan.yao@intel.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox