Re: [PATCH v4 6/6] powerpc/kvm-hv-pmu: Add perf-events for Hostwide counters

Vaibhav Jain <[email protected]> Tue, 11 Mar 2025 15:02:06 +0530
Newsgroups org.kernel.vger.kvm-ppc,org.kernel.vger.kvm,org.ozlabs.lists.linuxppc-dev
Message-ID <[email protected]>
Athira Rajeev <[email protected]> writes:

>> On 24 Feb 2025, at 6:45=E2=80=AFPM, Vaibhav Jain <[email protected]>=
 wrote:
>>=20
>> Update 'kvm-hv-pmu.c' to add five new perf-events mapped to the five
>> Hostwide counters. Since these newly introduced perf events are at system
>> wide scope and can be read from any L1-Lpar CPU, 'kvmppc_pmu' scope and
>> capabilities are updated appropriately.
>>=20
>> Also introduce two new helpers. First is kvmppc_update_l0_stats() that u=
ses
>> the infrastructure introduced in previous patches to issues the
>> H_GUEST_GET_STATE hcall L0-PowerVM to fetch guest-state-buffer holding t=
he
>> latest values of these counters which is then parsed and 'l0_stats'
>> variable updated.
>>=20
>> Second helper is kvmppc_pmu_event_update() which is called from
>> 'kvmppv_pmu' callbacks and uses kvmppc_update_l0_stats() to update
>> 'l0_stats' and the update the 'struct perf_event's event-counter.
>>=20
>> Some minor updates to kvmppc_pmu_{add, del, read}() to remove some debug
>> scaffolding code.
>>=20
>> Signed-off-by: Vaibhav Jain <[email protected]>
>> ---
>> Changelog
>>=20
>> v3->v4:
>> * Minor tweaks to patch description and code as its now being built as a
>> separate kernel module.
>>=20
>> v2->v3:
>> None
>>=20
>> v1->v2:
>> None
>> ---
>> arch/powerpc/perf/kvm-hv-pmu.c | 92 +++++++++++++++++++++++++++++++++-
>> 1 file changed, 91 insertions(+), 1 deletion(-)
>>=20
>> diff --git a/arch/powerpc/perf/kvm-hv-pmu.c b/arch/powerpc/perf/kvm-hv-p=
mu.c
>> index ed371454f7b5..274459bb32d6 100644
>> --- a/arch/powerpc/perf/kvm-hv-pmu.c
>> +++ b/arch/powerpc/perf/kvm-hv-pmu.c
>> @@ -30,6 +30,11 @@
>> #include "asm/guest-state-buffer.h"
>>=20
>> enum kvmppc_pmu_eventid {
>> + KVMPPC_EVENT_HOST_HEAP,
>> + KVMPPC_EVENT_HOST_HEAP_MAX,
>> + KVMPPC_EVENT_HOST_PGTABLE,
>> + KVMPPC_EVENT_HOST_PGTABLE_MAX,
>> + KVMPPC_EVENT_HOST_PGTABLE_RECLAIM,
>> KVMPPC_EVENT_MAX,
>> };
>>=20
>> @@ -61,8 +66,14 @@ static DEFINE_SPINLOCK(lock_l0_stats);
>> /* GSB related structs needed to talk to L0 */
>> static struct kvmppc_gs_msg *gsm_l0_stats;
>> static struct kvmppc_gs_buff *gsb_l0_stats;
>> +static struct kvmppc_gs_parser gsp_l0_stats;
>>=20
>> static struct attribute *kvmppc_pmu_events_attr[] =3D {
>> + KVMPPC_PMU_EVENT_ATTR(host_heap, KVMPPC_EVENT_HOST_HEAP),
>> + KVMPPC_PMU_EVENT_ATTR(host_heap_max, KVMPPC_EVENT_HOST_HEAP_MAX),
>> + KVMPPC_PMU_EVENT_ATTR(host_pagetable, KVMPPC_EVENT_HOST_PGTABLE),
>> + KVMPPC_PMU_EVENT_ATTR(host_pagetable_max, KVMPPC_EVENT_HOST_PGTABLE_MA=
X),
>> + KVMPPC_PMU_EVENT_ATTR(host_pagetable_reclaim, KVMPPC_EVENT_HOST_PGTABL=
E_RECLAIM),
>> NULL,
>> };
>>=20
>> @@ -71,7 +82,7 @@ static const struct attribute_group kvmppc_pmu_events_=
group =3D {
>> .attrs =3D kvmppc_pmu_events_attr,
>> };
>>=20
>> -PMU_FORMAT_ATTR(event, "config:0");
>> +PMU_FORMAT_ATTR(event, "config:0-5");
>> static struct attribute *kvmppc_pmu_format_attr[] =3D {
>> &format_attr_event.attr,
>> NULL,
>> @@ -88,6 +99,79 @@ static const struct attribute_group *kvmppc_pmu_attr_=
groups[] =3D {
>> NULL,
>> };
>>=20
>> +/*
>> + * Issue the hcall to get the L0-host stats.
>> + * Should be called with l0-stat lock held
>> + */
>> +static int kvmppc_update_l0_stats(void)
>> +{
>> + int rc;
>> +
>> + /* With HOST_WIDE flags guestid and vcpuid will be ignored */
>> + rc =3D kvmppc_gsb_recv(gsb_l0_stats, KVMPPC_GS_FLAGS_HOST_WIDE);
>> + if (rc)
>> + goto out;
>> +
>> + /* Parse the guest state buffer is successful */
>> + rc =3D kvmppc_gse_parse(&gsp_l0_stats, gsb_l0_stats);
>> + if (rc)
>> + goto out;
>> +
>> + /* Update the l0 returned stats*/
>> + memset(&l0_stats, 0, sizeof(l0_stats));
>> + rc =3D kvmppc_gsm_refresh_info(gsm_l0_stats, gsb_l0_stats);
>> +
>> +out:
>> + return rc;
>> +}
>> +
>> +/* Update the value of the given perf_event */
>> +static int kvmppc_pmu_event_update(struct perf_event *event)
>> +{
>> + int rc;
>> + u64 curr_val, prev_val;
>> + unsigned long flags;
>> + unsigned int config =3D event->attr.config;
>> +
>> + /* Ensure no one else is modifying the l0_stats */
>> + spin_lock_irqsave(&lock_l0_stats, flags);
>> +
>> + rc =3D kvmppc_update_l0_stats();
>> + if (!rc) {
>> + switch (config) {
>> + case KVMPPC_EVENT_HOST_HEAP:
>> + curr_val =3D l0_stats.guest_heap;
>> + break;
>> + case KVMPPC_EVENT_HOST_HEAP_MAX:
>> + curr_val =3D l0_stats.guest_heap_max;
>> + break;
>> + case KVMPPC_EVENT_HOST_PGTABLE:
>> + curr_val =3D l0_stats.guest_pgtable_size;
>> + break;
>> + case KVMPPC_EVENT_HOST_PGTABLE_MAX:
>> + curr_val =3D l0_stats.guest_pgtable_size_max;
>> + break;
>> + case KVMPPC_EVENT_HOST_PGTABLE_RECLAIM:
>> + curr_val =3D l0_stats.guest_pgtable_reclaim;
>> + break;
>> + default:
>> + rc =3D -ENOENT;
>> + break;
>> + }
>> + }
>> +
>> + spin_unlock_irqrestore(&lock_l0_stats, flags);
>> +
>> + /* If no error than update the perf event */
>> + if (!rc) {
>> + prev_val =3D local64_xchg(&event->hw.prev_count, curr_val);
>> + if (curr_val > prev_val)
>> + local64_add(curr_val - prev_val, &event->count);
>> + }
>> +
>> + return rc;
>> +}
>> +
>> static int kvmppc_pmu_event_init(struct perf_event *event)
>> {
>> unsigned int config =3D event->attr.config;
>> @@ -110,15 +194,19 @@ static int kvmppc_pmu_event_init(struct perf_event=
 *event)
>>=20
>> static void kvmppc_pmu_del(struct perf_event *event, int flags)
>> {
>> + /* Do nothing */
>> }
>
> If we don=E2=80=99t read the counter stats in =E2=80=9Cdel=E2=80=9D call =
back, we will loose the final count getting updated, right ?
> Del callback needs to call kvmppc_pmu_read. Can you check the difference =
in count stats by calling kvmppc_pmu_read here ?
>

Yes, agreed. Will address this in next version of the patch series

> Thanks
> Athira
>
>>=20
>> static int kvmppc_pmu_add(struct perf_event *event, int flags)
>> {
>> + if (flags & PERF_EF_START)
>> + return kvmppc_pmu_event_update(event);
>> return 0;
>> }
>>=20
>> static void kvmppc_pmu_read(struct perf_event *event)
>> {
>> + kvmppc_pmu_event_update(event);
>> }
>>=20
>> /* Return the size of the needed guest state buffer */
>> @@ -302,6 +390,8 @@ static struct pmu kvmppc_pmu =3D {
>> .read =3D kvmppc_pmu_read,
>> .attr_groups =3D kvmppc_pmu_attr_groups,
>> .type =3D -1,
>> + .scope =3D PERF_PMU_SCOPE_SYS_WIDE,
>> + .capabilities =3D PERF_PMU_CAP_NO_EXCLUDE | PERF_PMU_CAP_NO_INTERRUPT,
>> };
>>=20
>> static int __init kvmppc_register_pmu(void)
>> --=20
>> 2.48.1
>>=20
>>=20
>>=20
>
>

--=20
Cheers
~ Vaibhav