All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jonathan Cameron <jic23@kernel.org>
To: Dave Jiang <dave.jiang@intel.com>
Cc: linux-cxl@vger.kernel.org, linux-perf-users@vger.kernel.org,
	will@kernel.org, mark.rutland@arm.com, dave@stgolabs.net,
	sashiko-bot@kernel.org
Subject: Re: [PATCH v2 3/9] perf/cxl: Fix the counter overflow delta fixup
Date: Wed, 29 Jul 2026 23:21:42 +0100	[thread overview]
Message-ID: <20260729232142.050ed34c@jic23-huawei> (raw)
In-Reply-To: <20260729145555.3919550-4-dave.jiang@intel.com>

On Wed, 29 Jul 2026 07:55:49 -0700
Dave Jiang <dave.jiang@intel.com> wrote:

> Counters are configured with Freeze on Overflow and are never reloaded: on
> overflow the counter wraps to 0, counts on until the CPMU freezes, and
Hi Dave,

Thanks for looking at these.

Why would it count on if it froze?  I think the bot is tripping over the
fact we don't yet implement free running counters (and the other bug
about not unfreezing) for currently the ability to freeze on only some
counters (to do periodic sampling for instance).

The CXL CPMU spec is incredibly broad in what is supported, so maybe
we want to harden things anyway but I'm not sure the condition described
by most of this is real.

> retains that residual (CXL r4.0 8.2.7.2.3). So the masked subtraction in
> __cxl_pmu_read() only spans the wrap when new_cnt < prev_cnt.

It's been a long time so maybe I have how this was meant to work wrong.

There are two paths to __cxl_pmu_read()

1. We have freeze on overflow enabled so any counter that overflows results
in an interrupt. At that point all counters are frozen.
We then read only the counter that overflowed (which is 0) and that
will update the prev_cnt storage.  No chance of hitting the full wrap
around seen here.

2. An on demand read (polling) In this case the counter may
take any value, but because we have freeze on overflow it can't have
wrapped (as otherwise we'd have taken path 1).

So slightly fun question of why we have any wrapping control and I think
the answer is because the freeze on overflow isn't very specific in the
spec for whether it freezes on max value or 0.


> 
> The fixup keys off the delta rather than the operands:
> 
> 	delta = (new_cnt - prev_cnt) & GENMASK_ULL(counter_width - 1, 0);
> 	if (overflow && delta < GENMASK_ULL(counter_width - 1, 0))
> 		delta += (1UL << counter_width);
> 
> and is wrong both ways. After a mid-period read the subtraction already
> spans the wrap, but the guard holds for all but one delta value, so a
> second period is added and the event over-counts. After an earlier
> overflow read - or after event_start(), which leaves prev_count at 0 -
> prev_cnt is a small residual, and once the new residual is greater or
> equal only the difference between the two is counted and the period is
> lost, which for a residual of 0 means every period after the first.
> 'perf stat -I' hits the former, plain 'perf stat' the latter.
> 
> The shift is also undefined for counter_width == 64, and for >= 32 on
> 32-bit kernels where 1UL is 32 bits.

That bit seems valid.

> 
> Condition the fixup on new_cnt >= prev_cnt, the one case the masked
> subtraction cannot express, and use mask + 1 for the period: it is 0 for a
> 64-bit counter, the correct sum mod 2^64, and needs no shift. A
> userspace read landing between the wrap and the handler clearing the
> overflow status stays correct because the counter is frozen and cannot
> advance between the two reads.
> 
> Fixes: 5d7107c72796 ("perf: CXL Performance Monitoring Unit driver")
> Reported-by: sashiko-bot@kernel.org
> Closes: https://sashiko.dev/#/patchset/20260715191454.459673-1-dave@stgolabs.net?part=1
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Dave Jiang <dave.jiang@intel.com>
> ---
> v2:
> - Fix the fixup instead of deleting it. v1 dropped it on the rationale
>   that the masked subtraction already handles the wrap, which only holds
>   for new_cnt < prev_cnt; with freeze on overflow and no reload the
>   overflow path routinely sees new_cnt >= prev_cnt, where v1 silently
>   dropped a full period (sashiko-bot,
>   https://lore.kernel.org/linux-cxl/20260728211411.47B2D1F000E9@smtp.kernel.org/).
> - Keep the overflow argument and the __cxl_pmu_read()/cxl_pmu_read()
>   split; v1 folded them together once the argument went unused.
> - Use mask + 1 rather than 1ULL << counter_width so the 64-bit counter
>   case needs no special casing.
> ---
>  drivers/perf/cxl_pmu.c | 18 ++++++++++++------
>  1 file changed, 12 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/perf/cxl_pmu.c b/drivers/perf/cxl_pmu.c
> index 3138514157cd..956da8202551 100644
> --- a/drivers/perf/cxl_pmu.c
> +++ b/drivers/perf/cxl_pmu.c
> @@ -690,7 +690,7 @@ static void __cxl_pmu_read(struct perf_event *event, bool overflow)
>  {
>  	struct cxl_pmu_info *info = pmu_to_cxl_pmu_info(event->pmu);
>  	struct hw_perf_event *hwc = &event->hw;
> -	u64 new_cnt, prev_cnt, delta;
> +	u64 new_cnt, prev_cnt, delta, mask;
>  
>  	do {
>  		prev_cnt = local64_read(&hwc->prev_count);
> @@ -698,12 +698,18 @@ static void __cxl_pmu_read(struct perf_event *event, bool overflow)
>  	} while (local64_cmpxchg(&hwc->prev_count, prev_cnt, new_cnt) != prev_cnt);
>  
>  	/*
> -	 * If we know an overflow occur then take that into account.
> -	 * Note counter is not reset as that would lose events
> +	 * The counter wraps to 0 and is never reloaded, so the masked unsigned
> +	 * subtraction only recovers a wrap when new_cnt < prev_cnt. On the
> +	 * overflow path new_cnt >= prev_cnt means the full period elapsed
> +	 * (prev_cnt is 0 from event_start(), or the residual left by an earlier
> +	 * overflow), so add the period back. mask + 1 is 2^counter_width, which
> +	 * evaluates to 0 for a 64-bit counter - the correct sum mod 2^64, and
> +	 * avoids the undefined 1 << 64.
>  	 */
> -	delta = (new_cnt - prev_cnt) & GENMASK_ULL(info->counter_width - 1, 0);
> -	if (overflow && delta < GENMASK_ULL(info->counter_width - 1, 0))
> -		delta += (1UL << info->counter_width);
> +	mask = GENMASK_ULL(info->counter_width - 1, 0);
> +	delta = (new_cnt - prev_cnt) & mask;
> +	if (overflow && new_cnt >= prev_cnt)
> +		delta += mask + 1;
>  
>  	local64_add(delta, &event->count);
>  }


  parent reply	other threads:[~2026-07-29 22:21 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-29 14:55 [PATCH v2 0/9] perf/cxlpmu: Misc sashiko raised issues fixes Dave Jiang
2026-07-29 14:55 ` [PATCH v2 1/9] perf/cxl: Program the requested event group on configurable counters Dave Jiang
2026-07-29 15:06   ` sashiko-bot
2026-07-29 22:29   ` Jonathan Cameron
2026-07-29 14:55 ` [PATCH v2 2/9] perf/cxl: Clear stale event fields before reprogramming a counter Dave Jiang
2026-07-29 15:08   ` sashiko-bot
2026-07-29 22:25   ` Jonathan Cameron
2026-07-29 14:55 ` [PATCH v2 3/9] perf/cxl: Fix the counter overflow delta fixup Dave Jiang
2026-07-29 15:13   ` sashiko-bot
2026-07-29 22:21   ` Jonathan Cameron [this message]
2026-07-29 14:55 ` [PATCH v2 4/9] perf/cxl: Accept an overflow interrupt on MSI message number 0 Dave Jiang
2026-07-29 19:28   ` Jonathan Cameron
2026-07-29 19:59   ` Davidlohr Bueso
2026-07-29 14:55 ` [PATCH v2 5/9] perf/cxl: Keep the overflow interrupt pinned to the managed CPU Dave Jiang
2026-07-29 15:23   ` sashiko-bot
2026-07-29 19:25   ` Jonathan Cameron
2026-07-29 20:27   ` Davidlohr Bueso
2026-07-29 14:55 ` [PATCH v2 6/9] perf/cxl: Unfreeze counters after handling an overflow interrupt Dave Jiang
2026-07-29 19:24   ` Jonathan Cameron
2026-07-29 14:55 ` [PATCH v2 7/9] perf/cxl: Validate the hardware-reported counter width Dave Jiang
2026-07-29 15:11   ` sashiko-bot
2026-07-29 19:21   ` Jonathan Cameron
2026-07-29 14:55 ` [PATCH v2 8/9] perf/cxl: Don't use pmu.dev in IRQ and hotplug callbacks after unregister Dave Jiang
2026-07-29 15:34   ` sashiko-bot
2026-07-29 19:17   ` Jonathan Cameron
2026-07-29 14:55 ` [PATCH v2 9/9] perf/cxl: Avoid cpumask_of(-1) when no CPU is assigned Dave Jiang
2026-07-29 15:19   ` sashiko-bot
2026-07-29 19:14   ` Jonathan Cameron

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=20260729232142.050ed34c@jic23-huawei \
    --to=jic23@kernel.org \
    --cc=dave.jiang@intel.com \
    --cc=dave@stgolabs.net \
    --cc=linux-cxl@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=sashiko-bot@kernel.org \
    --cc=will@kernel.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.