All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Ian Rogers" <irogers@google.com>
Cc: linux-perf-users@vger.kernel.org
Subject: Re: [PATCH v4 1/2] perf: Add Raspberry Pi BCM2835 AXI PMU driver
Date: Wed, 12 Aug 2026 05:36:49 +0000	[thread overview]
Message-ID: <20260812053649.69DC01F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260812052417.2361040-2-irogers@google.com>

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] Transient IPC mailbox errors corrupt PMU counter values with massive positive spikes.
- [High] Module unload triggers a Use-After-Free in CPU hotplug callback.
- [High] Global hardware monitor is never enabled for batched or grouped event additions.
- [High] Concurrent timer manipulation triggers WARN_ON_ONCE and timer failure.
- [Medium] Lockless modification of event->hw.state overwrites PERF_HES_STOPPED flag.
- [Medium] VPU hardware monitor events fail to collect data for up to 2 seconds due to missing explicit scheduling.
- [Medium] Unconditional lock drop in worker thread causes lockdep splat and unlocked MMIO access on fallback paths.
--

--- Patch [1]: [PATCH v4 1/2] perf: Add Raspberry Pi BCM2835 AXI PMU driver ---
commit 8696502c11139b0621905345c84a98e3653345a3
Author: Ian Rogers <irogers@google.com>

perf: Add Raspberry Pi BCM2835 AXI PMU driver

This commit adds an uncore performance monitoring unit (PMU) driver
for Broadcom BCM2835-BCM2711 AXI system monitors and VideoCore VPU firmware
mailbox monitors found on Raspberry Pi 1 through 4.

> diff --git a/drivers/perf/rpi_axi_pmu.c b/drivers/perf/rpi_axi_pmu.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/perf/rpi_axi_pmu.c

[ ... ]

> +static void rpi_axi_pmu__exit(struct rpi_axi_pmu *pmu)
> +{
> +	perf_pmu_unregister(&pmu->pmu);
> +	cpuhp_state_remove_instance(rpi_axi_pmu_cpuhp_state, &pmu->cpuhp_node);

[Severity: High]
The perf_pmu_unregister() call frees the PMU structures, but
cpuhp_state_remove_instance() will subsequently invoke
rpi_axi_pmu_offline_cpu() on active CPUs.

In rpi_axi_pmu_offline_cpu(), we see:

perf_pmu_migrate_context(&pmu->pmu, cpu, target);

Could this cause a use-after-free by dereferencing pmu->pmu after it has
been unregistered? Should the cpuhp instance be removed before unregistering
the PMU?

[ ... ]

> +static void rpi_axi_pmu_enable_bus_watcher(struct rpi_axi_pmu *pmu, enum monitor mon,
> +					    int idx, int bus, int filter)
> +{
> +	int bus_control;
> +
> +	if (pmu->monitor[mon].hw_events.enabled[idx])
> +		return;
> +
> +	bus_control = BW_CTRL_ENABLE_BIT | (bus & 0x3F);
> +	if (filter) {
> +		bus_control |= BW_CTRL_ENABLE_ID_FILTER_BIT;
> +		bus_control |= (filter & 0x1F) << BW_CTRL_BUS_FILTER_SHIFT;
> +	}
> +	set_bus_watcher_control(pmu, mon, idx, BW_CTRL_RESET_BIT);
> +	set_bus_watcher_control(pmu, mon, idx, bus_control);
> +
> +	if (pmu->monitor[mon].hw_events.num_monitored == 1) {
> +		set_monitor_control(pmu, mon, GEN_CTL_RESET_BIT);
> +		set_monitor_control(pmu, mon, GEN_CTL_ENABLE_BIT | GEN_CTL_WATCH_BIT);
> +	}
> +}

[Severity: High]
If grouped events are added, rpi_axi_pmu_add() will increment
num_monitored multiple times before rpi_axi_pmu_start() is called for
the group. 

When start() invokes this function, num_monitored could already be greater
than 1. Does this mean the global monitor control will never be enabled for
grouped event additions, leaving the hardware disabled?

[ ... ]

> +static u32 rpi_axi_pmu_read_counter(struct rpi_axi_pmu *pmu, enum monitor mon, int idx,
> +				    enum counter counter)
> +{

[ ... ]

> +		err = rpi_firmware_property(pmu->firmware,
> +					    RPI_FIRMWARE_GET_PERIPH_REG,
> +					    tmp, sizeof(tmp));
> +
> +		if (err < 0 || tmp[1] != 1) {
> +			dev_err(&pmu->pdev->dev, "Failed to read bus watcher\n");
> +			return 0;
> +		}
> +		ret = tmp[2] & 0x7FFFFFFF;

[Severity: High]
If the mailbox communication fails, this function returns 0. Over in
rpi_axi_pmu_vpu_work_handler(), this 0 is blindly used to calculate the new
count:

new_count = rpi_axi_pmu_read_counter(pmu, MON__VPU, idx, counter);
...
prev_count = local64_read(&event->hw.prev_count);
delta = (new_count - prev_count) & 0x7FFFFFFF;
local64_add(delta, &event->count);

Can this lead to a massive positive spike in the counter value when
transient IPC errors occur, since 0 minus prev_count evaluates to a huge
positive delta?

[ ... ]

> +static void rpi_axi_pmu_vpu_work_handler(struct work_struct *work)
> +{

[ ... ]

> +		/* If VPU bus watcher is not enabled on hardware, enable it in process context */
> +		if (!pmu->monitor[MON__VPU].hw_events.enabled[idx]) {
> +			int bus = pmu->monitor[MON__VPU].hw_events.monitored_bus[idx];
> +			int filter = pmu->monitor[MON__VPU].hw_events.filter[idx];
> +
> +			spin_unlock_irq(&pmu->lock);
> +			rpi_axi_pmu_enable_bus_watcher(pmu, MON__VPU, idx, bus, filter);
> +			spin_lock_irq(&pmu->lock);
> +		}

[Severity: Medium]
This drops the spinlock unconditionally. However, if the VPU monitor is
falling back to direct MMIO where use_mailbox_interface is false,
rpi_axi_pmu_enable_bus_watcher() calls set_bus_watcher_control() which
asserts that pmu->lock is held.

Does this lock drop violate the lockdep assertion and risk data races on
non-firmware platforms?

[ ... ]

> +static enum hrtimer_restart rpi_axi_pmu_timer_handler(struct hrtimer *timer)
> +{

[ ... ]

> +	if (pmu->active_vpu_events > 0)
> +		schedule_work(&pmu->vpu_work);
> +	spin_unlock_irqrestore(&pmu->lock, flags);
> +
> +	hrtimer_forward_now(timer, RPI_AXI_PMU_TIMER_INTERVAL);
> +	return HRTIMER_RESTART;
> +}

[Severity: High]
The spinlock is dropped before calling hrtimer_forward_now(). If a
concurrent delete and add operation occurs on another CPU during this
window, could hrtimer_start() be called while the timer is still processing?

If hrtimer_start() enqueues the timer concurrently, won't
hrtimer_forward_now() run on an already queued timer and trigger a warning
in the hrtimer core?

[ ... ]

> +static void rpi_axi_pmu_start(struct perf_event *event, int flags)
> +{
> +	struct rpi_axi_pmu *pmu = pmu_to_rpi_axi_pmu(event->pmu);
> +	enum monitor mon = config_to_monitor(event->attr.config);
> +	enum counter counter = config_to_counter(event->attr.config);
> +	unsigned long spinflags;
> +
> +	spin_lock_irqsave(&pmu->lock, spinflags);
> +	event->hw.state = 0;
> +	pmu->monitor[mon].hw_events.num_started++;
> +	if (mon == MON__SYSTEM) {
> +		int bus = pmu->monitor[mon].hw_events.monitored_bus[event->hw.idx];

[Severity: Medium]
When mon is MON__VPU, the hardware enablement is delegated entirely to
pmu->vpu_work. However, schedule_work() is not explicitly called here.

Does this mean VPU monitoring relies entirely on the periodic 2-second
hrtimer to start collecting data?

If so, won't short-lived perf workloads report 0 counts for VPU events
because the hardware takes up to 2 seconds to be enabled?

[ ... ]

> +static void rpi_axi_pmu_stop(struct perf_event *event, int flags)
> +{

[ ... ]

> +	if (event->hw.state & PERF_HES_STOPPED)
> +		return;
> +
> +	if (flags & PERF_EF_UPDATE) {
> +		rpi_axi_pmu_read(event);
> +		event->hw.state |= PERF_HES_UPTODATE;
> +	}
> +
> +	spin_lock_irqsave(&pmu->lock, spinflags);
> +	event->hw.state |= PERF_HES_STOPPED;

[Severity: Medium]
rpi_axi_pmu_stop() modifies event->hw.state to add PERF_HES_UPTODATE
without holding the spinlock.

Meanwhile, rpi_axi_pmu_vpu_work_handler() performs read-modify-write
operations on event->hw.state under the lock, such as:

event->hw.state |= PERF_HES_UPTODATE;

If stop() sets PERF_HES_STOPPED under the lock shortly after the worker
reads the old state, will the worker thread overwrite the state and
accidentally clear the PERF_HES_STOPPED flag when it writes back?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260812052417.2361040-1-irogers@google.com?part=1

  reply	other threads:[~2026-08-12  5:36 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-11  8:38 [PATCH v1 0/2] perf: Add Raspberry Pi AXI PMU driver Ian Rogers
2026-08-11  8:38 ` [PATCH v1 1/2] perf: Add Raspberry Pi BCM2835 " Ian Rogers
2026-08-11 14:32   ` Uwe Kleine-König
2026-08-11  8:38 ` [PATCH v1 2/2] perf: Add Raspberry Pi 5 (BCM2712) AXI PMU support Ian Rogers
2026-08-11 23:51 ` [PATCH v2 0/2] perf: Add Raspberry Pi AXI PMU driver Ian Rogers
2026-08-11 23:51   ` [PATCH v2 1/2] perf: Add Raspberry Pi BCM2835 " Ian Rogers
2026-08-12  0:04     ` sashiko-bot
2026-08-11 23:51   ` [PATCH v2 2/2] perf: Add Raspberry Pi 5 (BCM2712) AXI PMU support Ian Rogers
2026-08-12  0:05     ` sashiko-bot
2026-08-12  0:27   ` [PATCH v3 0/2] perf: Add Raspberry Pi AXI PMU driver Ian Rogers
2026-08-12  0:27     ` [PATCH v3 1/2] perf: Add Raspberry Pi BCM2835 " Ian Rogers
2026-08-12  0:40       ` sashiko-bot
2026-08-12  0:27     ` [PATCH v3 2/2] perf: Add Raspberry Pi 5 (BCM2712) AXI PMU support Ian Rogers
2026-08-12  0:40       ` sashiko-bot
2026-08-12  5:24     ` [PATCH v4 0/2] perf: Add Raspberry Pi AXI PMU driver Ian Rogers
2026-08-12  5:24       ` [PATCH v4 1/2] perf: Add Raspberry Pi BCM2835 " Ian Rogers
2026-08-12  5:36         ` sashiko-bot [this message]
2026-08-12  5:24       ` [PATCH v4 2/2] perf: Add Raspberry Pi 5 (BCM2712) AXI PMU support Ian Rogers
2026-08-12  5:34         ` sashiko-bot

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=20260812053649.69DC01F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=irogers@google.com \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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.