* [PATCH 0/3] Rework panthor's cache flush and soft reset locking
@ 2026-07-29 6:19 Nicolas Frattaroli
2026-07-29 6:19 ` [PATCH 1/3] wait: Introduce non-irq variants of wait_event_lock_timeout Nicolas Frattaroli
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Nicolas Frattaroli @ 2026-07-29 6:19 UTC (permalink / raw)
To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, K Prateek Nayak, Boris Brezillon,
Steven Price, Liviu Dudau, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Grant Likely,
Heiko Stuebner
Cc: linux-kernel, dri-devel, kernel, Nicolas Frattaroli
There are two problems in panthor's cache flushing/soft reset code
related to locking.
The first problem is that the spinlocks are taken with _irqsave, even
though the contended lock is never acquired in a raw interrupt handler.
Only a threaded handler locks it.
The second problem is that the wait_event condition references
pending_reqs without holding the required lock. This means there's a
data race.
While resolving the first problem is easy, the second problem requires
adding some more macro variants to wait.h, which is done in the first
patch.
New tracepoints to debug cache flushing duration without initial locking
waits is thrown in for good measure as well, to complement what's in
lock_stat and what the function tracer can already do.
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
Nicolas Frattaroli (3):
wait: Introduce non-irq variants of wait_event_lock_timeout
drm/panthor: Revisit reqs_lock handling in flush/reset paths
drm/panthor: Add tracepoints for cache flushing
drivers/gpu/drm/panthor/panthor_gpu.c | 28 +++++------
drivers/gpu/drm/panthor/panthor_trace.h | 58 +++++++++++++++++++++++
include/linux/wait.h | 84 +++++++++++++++++++++++++++++++++
3 files changed, 156 insertions(+), 14 deletions(-)
---
base-commit: f57436380d5de1c1ded898e21378b0b4159c8790
change-id: 20260728-panthor-cache-flush-fix-b36cb15f92c3
Best regards,
--
Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/3] wait: Introduce non-irq variants of wait_event_lock_timeout
2026-07-29 6:19 [PATCH 0/3] Rework panthor's cache flush and soft reset locking Nicolas Frattaroli
@ 2026-07-29 6:19 ` Nicolas Frattaroli
2026-07-29 10:10 ` Peter Zijlstra
2026-07-29 6:19 ` [PATCH 2/3] drm/panthor: Revisit reqs_lock handling in flush/reset paths Nicolas Frattaroli
2026-07-29 6:19 ` [PATCH 3/3] drm/panthor: Add tracepoints for cache flushing Nicolas Frattaroli
2 siblings, 1 reply; 8+ messages in thread
From: Nicolas Frattaroli @ 2026-07-29 6:19 UTC (permalink / raw)
To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, K Prateek Nayak, Boris Brezillon,
Steven Price, Liviu Dudau, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Grant Likely,
Heiko Stuebner
Cc: linux-kernel, dri-devel, kernel, Nicolas Frattaroli
For locks that are never acquired in an interrupt context, the non-irq
variants for spin_lock()/spin_unlock() are more efficient. However, so
far, wait.h only provided wait_event_lock_irq_timeout and
wait_event_interruptible_lock_irq_timeout, both of which use
spin_lock_irq()/spin_unlock_irq().
Rectify this by adding new non-irq variants of these two macros.
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
include/linux/wait.h | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)
diff --git a/include/linux/wait.h b/include/linux/wait.h
index dce055e6add3..cd9b989c120e 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -1174,6 +1174,13 @@ do { \
__ret = schedule_timeout(__ret); \
spin_lock_irq(&lock));
+#define __wait_event_lock_timeout(wq_head, condition, lock, timeout, state) \
+ ___wait_event(wq_head, ___wait_cond_timeout(condition), \
+ state, 0, timeout, \
+ spin_unlock(&lock); \
+ __ret = schedule_timeout(__ret); \
+ spin_lock(&lock))
+
/**
* wait_event_interruptible_lock_irq_timeout - sleep until a condition gets
* true or a timeout elapses. The condition is checked under
@@ -1219,6 +1226,83 @@ do { \
__ret; \
})
+/**
+ * wait_event_interruptible_lock_timeout - sleep until a condition gets
+ * true or a timeout elapses. The condition is checked under
+ * the lock with IRQs enabled. This is expected to be called
+ * with the lock taken.
+ * @wq_head: the waitqueue to wait on
+ * @condition: a C expression for the event to wait for
+ * @lock: a spinlock_t that has been locked with spin_lock(), which will be
+ * released before schedule() and reacquired afterwards.
+ * @timeout: timeout, in jiffies
+ *
+ * The process is put to sleep (TASK_INTERRUPTIBLE) until the
+ * @condition evaluates to true or signal is received. The @condition is
+ * checked each time the waitqueue @wq_head is woken up.
+ *
+ * wake_up() has to be called after changing any variable that could
+ * change the result of the wait condition.
+ *
+ * This is supposed to be called while holding the lock. The lock is
+ * dropped before going to sleep using spin_unlock() and is reacquired
+ * using spin_lock() afterwards, but without globally disabling IRQs.
+ * This is in contrast to wait_event_interruptible_lock_irq_timeout(),
+ * which uses the spin_lock_irq() and spin_unlock_irq() functions instead.
+ *
+ * The function returns 0 if the @timeout elapsed, -ERESTARTSYS if it
+ * was interrupted by a signal, and the remaining jiffies otherwise
+ * if the condition evaluated to true before the timeout elapsed.
+ */
+#define wait_event_interruptible_lock_timeout(wq_head, condition, lock, \
+ timeout) \
+({ \
+ long __ret = timeout; \
+ if (!___wait_cond_timeout(condition)) \
+ __ret = __wait_event_lock_timeout( \
+ wq_head, condition, lock, timeout, \
+ TASK_INTERRUPTIBLE); \
+ __ret; \
+})
+
+/**
+ * wait_event_lock_timeout - sleep until a condition gets true or a timeout
+ * elapses. The condition is checked under the lock with IRQs
+ * enabled. This is expected to be called with the lock taken.
+ * @wq_head: the waitqueue to wait on
+ * @condition: a C expression for the event to wait for
+ * @lock: a spinlock_t that has been locked with spin_lock(), which will be
+ * released before schedule() and reacquired afterwards.
+ * @timeout: timeout, in jiffies
+ *
+ * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the
+ * @condition evaluates to true or signal is received. The @condition is
+ * checked each time the waitqueue @wq_head is woken up.
+ *
+ * wake_up() has to be called after changing any variable that could
+ * change the result of the wait condition.
+ *
+ * This is supposed to be called while holding the lock. The lock is
+ * dropped before going to sleep using spin_unlock() and is reacquired
+ * using spin_lock() afterwards, but without globally disabling IRQs.
+ * This is in contrast to wait_event_lock_irq_timeout(), which uses the
+ * spin_lock_irq() and spin_unlock_irq() functions instead. Compared to
+ * wait_event_interruptible_lock_timeout(), it uses the %TASK_UNINTERRUPTIBLE
+ * instead of allowing the task to be interrupted.
+ *
+ * The function returns 0 if the @timeout elapsed, or the remaining jiffies
+ * otherwise if the condition evaluated to true before the timeout elapsed.
+ */
+#define wait_event_lock_timeout(wq_head, condition, lock, timeout) \
+({ \
+ long __ret = timeout; \
+ if (!___wait_cond_timeout(condition)) \
+ __ret = __wait_event_lock_timeout( \
+ wq_head, condition, lock, timeout, \
+ TASK_UNINTERRUPTIBLE); \
+ __ret; \
+})
+
/*
* Waitqueues which are removed from the waitqueue_head at wakeup time
*/
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/3] drm/panthor: Revisit reqs_lock handling in flush/reset paths
2026-07-29 6:19 [PATCH 0/3] Rework panthor's cache flush and soft reset locking Nicolas Frattaroli
2026-07-29 6:19 ` [PATCH 1/3] wait: Introduce non-irq variants of wait_event_lock_timeout Nicolas Frattaroli
@ 2026-07-29 6:19 ` Nicolas Frattaroli
2026-07-29 6:34 ` sashiko-bot
2026-07-29 6:19 ` [PATCH 3/3] drm/panthor: Add tracepoints for cache flushing Nicolas Frattaroli
2 siblings, 1 reply; 8+ messages in thread
From: Nicolas Frattaroli @ 2026-07-29 6:19 UTC (permalink / raw)
To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, K Prateek Nayak, Boris Brezillon,
Steven Price, Liviu Dudau, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Grant Likely,
Heiko Stuebner
Cc: linux-kernel, dri-devel, kernel, Nicolas Frattaroli
panthor_gpu_flush_caches() and panthor_gpu_soft_reset() would read (and
even reset) the contents of the pending_reqs register outside of holding
the reqs_lock. Additionally, when it did hold the lock, it did so with
the irqsave/irqrestore variants, even though the spinlock was never
acquired in an atomic context, just the threaded handler.
Use the new wait_event_lock_timeout() macro to check pending_reqs under
the lock, and only do so without disabling interrupts.
Fixes: 5cd894e258c4 ("drm/panthor: Add the GPU logical block")
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
drivers/gpu/drm/panthor/panthor_gpu.c | 25 +++++++++++--------------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
index c013d6bf9a59..f015bde80abf 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.c
+++ b/drivers/gpu/drm/panthor/panthor_gpu.c
@@ -330,35 +330,34 @@ int panthor_gpu_flush_caches(struct panthor_device *ptdev,
u32 l2, u32 lsc, u32 other)
{
struct panthor_gpu *gpu = ptdev->gpu;
- unsigned long flags;
int ret = 0;
/* Serialize cache flush operations. */
guard(mutex)(&ptdev->gpu->cache_flush_lock);
- spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
+ spin_lock(&ptdev->gpu->reqs_lock);
if (!(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED)) {
ptdev->gpu->pending_reqs |= GPU_IRQ_CLEAN_CACHES_COMPLETED;
gpu_write(gpu->iomem, GPU_CMD, GPU_FLUSH_CACHES(l2, lsc, other));
} else {
ret = -EIO;
}
- spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags);
- if (ret)
+ if (ret) {
+ spin_unlock(&ptdev->gpu->reqs_lock);
return ret;
+ }
- if (!wait_event_timeout(ptdev->gpu->reqs_acked,
+ if (!wait_event_lock_timeout(ptdev->gpu->reqs_acked,
!(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED),
- msecs_to_jiffies(100))) {
- spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
+ ptdev->gpu->reqs_lock, msecs_to_jiffies(100))) {
if ((ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED) != 0 &&
!(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_CLEAN_CACHES_COMPLETED))
ret = -ETIMEDOUT;
else
ptdev->gpu->pending_reqs &= ~GPU_IRQ_CLEAN_CACHES_COMPLETED;
- spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags);
}
+ spin_unlock(&ptdev->gpu->reqs_lock);
if (ret) {
panthor_device_schedule_reset(ptdev);
@@ -378,27 +377,25 @@ int panthor_gpu_soft_reset(struct panthor_device *ptdev)
{
struct panthor_gpu *gpu = ptdev->gpu;
bool timedout = false;
- unsigned long flags;
- spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
+ guard(spinlock)(&ptdev->gpu->reqs_lock);
+
if (!drm_WARN_ON(&ptdev->base,
ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED)) {
ptdev->gpu->pending_reqs |= GPU_IRQ_RESET_COMPLETED;
gpu_write(gpu->irq.iomem, INT_CLEAR, GPU_IRQ_RESET_COMPLETED);
gpu_write(gpu->iomem, GPU_CMD, GPU_SOFT_RESET);
}
- spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags);
- if (!wait_event_timeout(ptdev->gpu->reqs_acked,
+ if (!wait_event_lock_timeout(ptdev->gpu->reqs_acked,
!(ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED),
+ ptdev->gpu->reqs_lock,
msecs_to_jiffies(100))) {
- spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
if ((ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED) != 0 &&
!(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_RESET_COMPLETED))
timedout = true;
else
ptdev->gpu->pending_reqs &= ~GPU_IRQ_RESET_COMPLETED;
- spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags);
}
if (timedout) {
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/3] drm/panthor: Add tracepoints for cache flushing
2026-07-29 6:19 [PATCH 0/3] Rework panthor's cache flush and soft reset locking Nicolas Frattaroli
2026-07-29 6:19 ` [PATCH 1/3] wait: Introduce non-irq variants of wait_event_lock_timeout Nicolas Frattaroli
2026-07-29 6:19 ` [PATCH 2/3] drm/panthor: Revisit reqs_lock handling in flush/reset paths Nicolas Frattaroli
@ 2026-07-29 6:19 ` Nicolas Frattaroli
2026-07-29 17:10 ` Steven Rostedt
2 siblings, 1 reply; 8+ messages in thread
From: Nicolas Frattaroli @ 2026-07-29 6:19 UTC (permalink / raw)
To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
Valentin Schneider, K Prateek Nayak, Boris Brezillon,
Steven Price, Liviu Dudau, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Grant Likely,
Heiko Stuebner
Cc: linux-kernel, dri-devel, kernel, Nicolas Frattaroli
Add two new event tracepoints: gpu_cache_flush_start to be emitted after
acquiring the flush mutex and reqs spinlock, and gpu_cache_flush_end to
be emitted when leaving the function.
This allows debugging the duration a flush takes irrespective of initial
function entry lock contention by subtracting the start tracepoint's
timestamp from the end tracepoint timestamp, and additionally contains
information such as which caches were flushed.
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
drivers/gpu/drm/panthor/panthor_gpu.c | 3 ++
drivers/gpu/drm/panthor/panthor_trace.h | 58 +++++++++++++++++++++++++++++++++
2 files changed, 61 insertions(+)
diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
index f015bde80abf..a25955ad668b 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.c
+++ b/drivers/gpu/drm/panthor/panthor_gpu.c
@@ -336,6 +336,7 @@ int panthor_gpu_flush_caches(struct panthor_device *ptdev,
guard(mutex)(&ptdev->gpu->cache_flush_lock);
spin_lock(&ptdev->gpu->reqs_lock);
+ trace_gpu_cache_flush_start(ptdev->base.dev, l2, lsc, other);
if (!(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED)) {
ptdev->gpu->pending_reqs |= GPU_IRQ_CLEAN_CACHES_COMPLETED;
gpu_write(gpu->iomem, GPU_CMD, GPU_FLUSH_CACHES(l2, lsc, other));
@@ -345,6 +346,7 @@ int panthor_gpu_flush_caches(struct panthor_device *ptdev,
if (ret) {
spin_unlock(&ptdev->gpu->reqs_lock);
+ trace_gpu_cache_flush_end(ptdev->base.dev, l2, lsc, other);
return ret;
}
@@ -358,6 +360,7 @@ int panthor_gpu_flush_caches(struct panthor_device *ptdev,
ptdev->gpu->pending_reqs &= ~GPU_IRQ_CLEAN_CACHES_COMPLETED;
}
spin_unlock(&ptdev->gpu->reqs_lock);
+ trace_gpu_cache_flush_end(ptdev->base.dev, l2, lsc, other);
if (ret) {
panthor_device_schedule_reset(ptdev);
diff --git a/drivers/gpu/drm/panthor/panthor_trace.h b/drivers/gpu/drm/panthor/panthor_trace.h
index 6ffeb4fe6599..5e2a9b8d0481 100644
--- a/drivers/gpu/drm/panthor/panthor_trace.h
+++ b/drivers/gpu/drm/panthor/panthor_trace.h
@@ -76,6 +76,64 @@ TRACE_EVENT(gpu_job_irq,
__entry->events, __entry->duration_ns)
);
+/**
+ * gpu_cache_flush_start - called after cache flush locks taken, before flush
+ * @dev: pointer to the &struct device, for printing the device name
+ * @l2: "l2" flush flags
+ * @lsc: "lsc" flush flags
+ * @other: "other" flush flags
+ *
+ * Fires after any initial lock contention around the locks needed for flushing
+ * caches, but before the actual cache flush is requested.
+ */
+TRACE_EVENT(gpu_cache_flush_start,
+ TP_PROTO(const struct device *dev, u32 l2, u32 lsc, u32 other),
+ TP_ARGS(dev, l2, lsc, other),
+ TP_STRUCT__entry(
+ __string(dev_name, dev_name(dev))
+ __field(u32, l2)
+ __field(u32, lsc)
+ __field(u32, other)
+ ),
+ TP_fast_assign(
+ __assign_str(dev_name);
+ __entry->l2 = l2;
+ __entry->lsc = lsc;
+ __entry->other = other;
+ ),
+ TP_printk("%s: l2=0x%x lsc=0x%x other=0x%x", __get_str(dev_name),
+ __entry->l2, __entry->lsc, __entry->other)
+);
+
+/**
+ * gpu_cache_flush_end - called after cache flush
+ * @dev: pointer to the &struct device, for printing the device name
+ * @l2: "l2" flush flags
+ * @lsc: "lsc" flush flags
+ * @other: "other" flush flags
+ *
+ * Fires after either the cache flush is complete, or has failed. Can be used
+ * together with gpu_cache_flush_start to get how long the flush has taken.
+ */
+TRACE_EVENT(gpu_cache_flush_end,
+ TP_PROTO(const struct device *dev, u32 l2, u32 lsc, u32 other),
+ TP_ARGS(dev, l2, lsc, other),
+ TP_STRUCT__entry(
+ __string(dev_name, dev_name(dev))
+ __field(u32, l2)
+ __field(u32, lsc)
+ __field(u32, other)
+ ),
+ TP_fast_assign(
+ __assign_str(dev_name);
+ __entry->l2 = l2;
+ __entry->lsc = lsc;
+ __entry->other = other;
+ ),
+ TP_printk("%s: l2=0x%x lsc=0x%x other=0x%x", __get_str(dev_name),
+ __entry->l2, __entry->lsc, __entry->other)
+);
+
#endif /* __PANTHOR_TRACE_H__ */
#undef TRACE_INCLUDE_PATH
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] drm/panthor: Revisit reqs_lock handling in flush/reset paths
2026-07-29 6:19 ` [PATCH 2/3] drm/panthor: Revisit reqs_lock handling in flush/reset paths Nicolas Frattaroli
@ 2026-07-29 6:34 ` sashiko-bot
2026-07-29 11:40 ` Nicolas Frattaroli
0 siblings, 1 reply; 8+ messages in thread
From: sashiko-bot @ 2026-07-29 6:34 UTC (permalink / raw)
To: Nicolas Frattaroli; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] The timeout fallback logic creates a TOCTOU race with the threaded IRQ handler, potentially causing subsequent requests to falsely succeed immediately.
- [High] If a soft reset actually times out in hardware, the pending flag is never cleared, permanently breaking all future soft reset attempts.
--
--- Patch [2]: [PATCH 2/3] drm/panthor: Revisit reqs_lock handling in flush/reset paths ---
commit 179344ff59dcf475c58ff1e22983a6fa7e1203f4
Author: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
drm/panthor: Revisit reqs_lock handling in flush/reset paths
This patch revisits the handling of the reqs_lock in the panthor driver's
flush and reset paths. It switches from using spin_lock_irqsave() around a
wait_event_timeout() block to using wait_event_lock_timeout() with standard
spinlocks. This ensures the pending_reqs register is read and updated under
the lock without unnecessarily disabling interrupts.
> diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
> index c013d6bf9a599..f015bde80abf0 100644
> --- a/drivers/gpu/drm/panthor/panthor_gpu.c
> +++ b/drivers/gpu/drm/panthor/panthor_gpu.c
[ ... ]
> @@ -330,35 +330,34 @@ int panthor_gpu_flush_caches(struct panthor_device *ptdev,
> u32 l2, u32 lsc, u32 other)
> {
[ ... ]
> - if (!wait_event_timeout(ptdev->gpu->reqs_acked,
> + if (!wait_event_lock_timeout(ptdev->gpu->reqs_acked,
> !(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED),
> - msecs_to_jiffies(100))) {
> - spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
> + ptdev->gpu->reqs_lock, msecs_to_jiffies(100))) {
> if ((ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED) != 0 &&
> !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_CLEAN_CACHES_COMPLETED))
> ret = -ETIMEDOUT;
> else
> ptdev->gpu->pending_reqs &= ~GPU_IRQ_CLEAN_CACHES_COMPLETED;
[Severity: High]
This isn't a bug introduced by this patch, but does this timeout fallback
logic create a race condition with the threaded IRQ handler?
If the timeout occurs because the threaded IRQ handler was delayed (e.g. by
high CPU load), the fallback path clears the pending_reqs flag based on
INT_RAWSTAT, but it doesn't clear the physical interrupt via INT_CLEAR.
When the threaded IRQ handler eventually runs, won't it read the pending
interrupt and clear pending_reqs again? If a subsequent request was submitted
in the meantime, could its pending flag be spuriously cleared, causing its
wait to falsely succeed immediately?
> - spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags);
> }
> + spin_unlock(&ptdev->gpu->reqs_lock);
>
> if (ret) {
> panthor_device_schedule_reset(ptdev);
[ ... ]
> @@ -378,27 +377,25 @@ int panthor_gpu_soft_reset(struct panthor_device *ptdev)
> {
[ ... ]
> - if (!wait_event_timeout(ptdev->gpu->reqs_acked,
> + if (!wait_event_lock_timeout(ptdev->gpu->reqs_acked,
> !(ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED),
> + ptdev->gpu->reqs_lock,
> msecs_to_jiffies(100))) {
> - spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
> if ((ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED) != 0 &&
> !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_RESET_COMPLETED))
> timedout = true;
> else
> ptdev->gpu->pending_reqs &= ~GPU_IRQ_RESET_COMPLETED;
[Severity: High]
This is the same pre-existing race condition as in panthor_gpu_flush_caches(),
where clearing the pending flag without clearing the hardware interrupt could
allow a subsequent soft reset to succeed spuriously.
> - spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags);
> }
>
> if (timedout) {
[Severity: High]
This is a pre-existing issue, but if a soft reset actually times out in
hardware and timedout is set to true, does this early return bypass
clearing GPU_IRQ_RESET_COMPLETED from pending_reqs?
Looking at the end of panthor_gpu_soft_reset(), there is a
ptdev->gpu->pending_reqs = 0; after the timeout check. If we return early
here via return -ETIMEDOUT, the pending flag is left set.
Would any subsequent calls to panthor_gpu_soft_reset() hit the drm_WARN_ON
check at the start of the function, skip issuing the hardware command, and
permanently fail with a timeout?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729-panthor-cache-flush-fix-v1-0-205921ed3c81@collabora.com?part=2
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/3] wait: Introduce non-irq variants of wait_event_lock_timeout
2026-07-29 6:19 ` [PATCH 1/3] wait: Introduce non-irq variants of wait_event_lock_timeout Nicolas Frattaroli
@ 2026-07-29 10:10 ` Peter Zijlstra
0 siblings, 0 replies; 8+ messages in thread
From: Peter Zijlstra @ 2026-07-29 10:10 UTC (permalink / raw)
To: Nicolas Frattaroli
Cc: Ingo Molnar, Juri Lelli, Vincent Guittot, Dietmar Eggemann,
Steven Rostedt, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Grant Likely, Heiko Stuebner, linux-kernel,
dri-devel, kernel
On Wed, Jul 29, 2026 at 08:19:49AM +0200, Nicolas Frattaroli wrote:
> For locks that are never acquired in an interrupt context, the non-irq
> variants for spin_lock()/spin_unlock() are more efficient. However, so
> far, wait.h only provided wait_event_lock_irq_timeout and
> wait_event_interruptible_lock_irq_timeout, both of which use
> spin_lock_irq()/spin_unlock_irq().
>
> Rectify this by adding new non-irq variants of these two macros.
>
> Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] drm/panthor: Revisit reqs_lock handling in flush/reset paths
2026-07-29 6:34 ` sashiko-bot
@ 2026-07-29 11:40 ` Nicolas Frattaroli
0 siblings, 0 replies; 8+ messages in thread
From: Nicolas Frattaroli @ 2026-07-29 11:40 UTC (permalink / raw)
To: sashiko-reviews; +Cc: dri-devel
On Wednesday, 29 July 2026 08:34:30 Central European Summer Time sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] The timeout fallback logic creates a TOCTOU race with the threaded IRQ handler, potentially causing subsequent requests to falsely succeed immediately.
> - [High] If a soft reset actually times out in hardware, the pending flag is never cleared, permanently breaking all future soft reset attempts.
> --
>
> --- Patch [2]: [PATCH 2/3] drm/panthor: Revisit reqs_lock handling in flush/reset paths ---
> commit 179344ff59dcf475c58ff1e22983a6fa7e1203f4
> Author: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
>
> drm/panthor: Revisit reqs_lock handling in flush/reset paths
>
> This patch revisits the handling of the reqs_lock in the panthor driver's
> flush and reset paths. It switches from using spin_lock_irqsave() around a
> wait_event_timeout() block to using wait_event_lock_timeout() with standard
> spinlocks. This ensures the pending_reqs register is read and updated under
> the lock without unnecessarily disabling interrupts.
>
> > diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
> > index c013d6bf9a599..f015bde80abf0 100644
> > --- a/drivers/gpu/drm/panthor/panthor_gpu.c
> > +++ b/drivers/gpu/drm/panthor/panthor_gpu.c
> [ ... ]
> > @@ -330,35 +330,34 @@ int panthor_gpu_flush_caches(struct panthor_device *ptdev,
> > u32 l2, u32 lsc, u32 other)
> > {
> [ ... ]
> > - if (!wait_event_timeout(ptdev->gpu->reqs_acked,
> > + if (!wait_event_lock_timeout(ptdev->gpu->reqs_acked,
> > !(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED),
> > - msecs_to_jiffies(100))) {
> > - spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
> > + ptdev->gpu->reqs_lock, msecs_to_jiffies(100))) {
> > if ((ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED) != 0 &&
> > !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_CLEAN_CACHES_COMPLETED))
> > ret = -ETIMEDOUT;
> > else
> > ptdev->gpu->pending_reqs &= ~GPU_IRQ_CLEAN_CACHES_COMPLETED;
>
> [Severity: High]
> This isn't a bug introduced by this patch, but does this timeout fallback
> logic create a race condition with the threaded IRQ handler?
>
> If the timeout occurs because the threaded IRQ handler was delayed (e.g. by
> high CPU load), the fallback path clears the pending_reqs flag based on
> INT_RAWSTAT, but it doesn't clear the physical interrupt via INT_CLEAR.
>
> When the threaded IRQ handler eventually runs, won't it read the pending
> interrupt and clear pending_reqs again? If a subsequent request was submitted
> in the meantime, could its pending flag be spuriously cleared, causing its
> wait to falsely succeed immediately?
Not that the special case logic isn't in need of some explanation which
I do not have handy, but: no, there's no pre-existing TOCTOU here as
far as I can tell. If the threaded handler didn't run, it wouldn't have
woken up the waitqueue here.
The fallback likely shouldn't exist in the first place, since it'd only
modify pending_reqs if the timeout and event wakeup coincided, but we
acquired the spinlock and pending_reqs still hasn't updated, which is
impossible, because the wakeup is fired by the threaded handler with
the spinlock held after pending_reqs is updated.
If `ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED) != 0` is
false, the else branch is essentially a no-op.
> > - spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags);
> > }
> > + spin_unlock(&ptdev->gpu->reqs_lock);
> >
> > if (ret) {
> > panthor_device_schedule_reset(ptdev);
> [ ... ]
> > @@ -378,27 +377,25 @@ int panthor_gpu_soft_reset(struct panthor_device *ptdev)
> > {
> [ ... ]
> > - if (!wait_event_timeout(ptdev->gpu->reqs_acked,
> > + if (!wait_event_lock_timeout(ptdev->gpu->reqs_acked,
> > !(ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED),
> > + ptdev->gpu->reqs_lock,
> > msecs_to_jiffies(100))) {
> > - spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
> > if ((ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED) != 0 &&
> > !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_RESET_COMPLETED))
> > timedout = true;
> > else
> > ptdev->gpu->pending_reqs &= ~GPU_IRQ_RESET_COMPLETED;
>
> [Severity: High]
> This is the same pre-existing race condition as in panthor_gpu_flush_caches(),
> where clearing the pending flag without clearing the hardware interrupt could
> allow a subsequent soft reset to succeed spuriously.
>
> > - spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags);
> > }
> >
> > if (timedout) {
>
> [Severity: High]
> This is a pre-existing issue, but if a soft reset actually times out in
> hardware and timedout is set to true, does this early return bypass
> clearing GPU_IRQ_RESET_COMPLETED from pending_reqs?
>
> Looking at the end of panthor_gpu_soft_reset(), there is a
> ptdev->gpu->pending_reqs = 0; after the timeout check. If we return early
> here via return -ETIMEDOUT, the pending flag is left set.
>
> Would any subsequent calls to panthor_gpu_soft_reset() hit the drm_WARN_ON
> check at the start of the function, skip issuing the hardware command, and
> permanently fail with a timeout?
>
The bigger problem is that none of the callers even care about whether
it timed out.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 3/3] drm/panthor: Add tracepoints for cache flushing
2026-07-29 6:19 ` [PATCH 3/3] drm/panthor: Add tracepoints for cache flushing Nicolas Frattaroli
@ 2026-07-29 17:10 ` Steven Rostedt
0 siblings, 0 replies; 8+ messages in thread
From: Steven Rostedt @ 2026-07-29 17:10 UTC (permalink / raw)
To: Nicolas Frattaroli
Cc: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Grant Likely, Heiko Stuebner, linux-kernel,
dri-devel, kernel
On Wed, 29 Jul 2026 08:19:51 +0200
Nicolas Frattaroli <nicolas.frattaroli@collabora.com> wrote:
> diff --git a/drivers/gpu/drm/panthor/panthor_trace.h b/drivers/gpu/drm/panthor/panthor_trace.h
> index 6ffeb4fe6599..5e2a9b8d0481 100644
> --- a/drivers/gpu/drm/panthor/panthor_trace.h
> +++ b/drivers/gpu/drm/panthor/panthor_trace.h
> @@ -76,6 +76,64 @@ TRACE_EVENT(gpu_job_irq,
> __entry->events, __entry->duration_ns)
> );
>
> +/**
> + * gpu_cache_flush_start - called after cache flush locks taken, before flush
> + * @dev: pointer to the &struct device, for printing the device name
> + * @l2: "l2" flush flags
> + * @lsc: "lsc" flush flags
> + * @other: "other" flush flags
> + *
> + * Fires after any initial lock contention around the locks needed for flushing
> + * caches, but before the actual cache flush is requested.
> + */
> +TRACE_EVENT(gpu_cache_flush_start,
> + TP_PROTO(const struct device *dev, u32 l2, u32 lsc, u32 other),
> + TP_ARGS(dev, l2, lsc, other),
> + TP_STRUCT__entry(
> + __string(dev_name, dev_name(dev))
> + __field(u32, l2)
> + __field(u32, lsc)
> + __field(u32, other)
> + ),
> + TP_fast_assign(
> + __assign_str(dev_name);
> + __entry->l2 = l2;
> + __entry->lsc = lsc;
> + __entry->other = other;
> + ),
> + TP_printk("%s: l2=0x%x lsc=0x%x other=0x%x", __get_str(dev_name),
> + __entry->l2, __entry->lsc, __entry->other)
> +);
> +
> +/**
> + * gpu_cache_flush_end - called after cache flush
> + * @dev: pointer to the &struct device, for printing the device name
> + * @l2: "l2" flush flags
> + * @lsc: "lsc" flush flags
> + * @other: "other" flush flags
> + *
> + * Fires after either the cache flush is complete, or has failed. Can be used
> + * together with gpu_cache_flush_start to get how long the flush has taken.
> + */
> +TRACE_EVENT(gpu_cache_flush_end,
> + TP_PROTO(const struct device *dev, u32 l2, u32 lsc, u32 other),
> + TP_ARGS(dev, l2, lsc, other),
> + TP_STRUCT__entry(
> + __string(dev_name, dev_name(dev))
> + __field(u32, l2)
> + __field(u32, lsc)
> + __field(u32, other)
> + ),
> + TP_fast_assign(
> + __assign_str(dev_name);
> + __entry->l2 = l2;
> + __entry->lsc = lsc;
> + __entry->other = other;
> + ),
> + TP_printk("%s: l2=0x%x lsc=0x%x other=0x%x", __get_str(dev_name),
> + __entry->l2, __entry->lsc, __entry->other)
> +);
> +
> #endif /* __PANTHOR_TRACE_H__ */
The above two are identical. You'll save memory by using a template instead:
DECLARE_EVENT_CLASS(gpu_cache_flush_template,
TP_PROTO(const struct device *dev, u32 l2, u32 lsc, u32 other),
TP_ARGS(dev, l2, lsc, other),
TP_STRUCT__entry(
__string(dev_name, dev_name(dev))
__field(u32, l2)
__field(u32, lsc)
__field(u32, other)
),
TP_fast_assign(
__assign_str(dev_name);
__entry->l2 = l2;
__entry->lsc = lsc;
__entry->other = other;
),
TP_printk("%s: l2=0x%x lsc=0x%x other=0x%x", __get_str(dev_name),
__entry->l2, __entry->lsc, __entry->other)
);
DEFINE_EVENT(gpu_cache_flush_template, gpu_cache_flush_start,
TP_PROTO(const struct device *dev, u32 l2, u32 lsc, u32 other),
TP_ARGS(dev, l2, lsc, other)
);
DEFINE_EVENT(gpu_cache_flush_template, gpu_cache_flush_end,
TP_PROTO(const struct device *dev, u32 l2, u32 lsc, u32 other),
TP_ARGS(dev, l2, lsc, other)
);
-- Steve
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-07-29 17:09 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29 6:19 [PATCH 0/3] Rework panthor's cache flush and soft reset locking Nicolas Frattaroli
2026-07-29 6:19 ` [PATCH 1/3] wait: Introduce non-irq variants of wait_event_lock_timeout Nicolas Frattaroli
2026-07-29 10:10 ` Peter Zijlstra
2026-07-29 6:19 ` [PATCH 2/3] drm/panthor: Revisit reqs_lock handling in flush/reset paths Nicolas Frattaroli
2026-07-29 6:34 ` sashiko-bot
2026-07-29 11:40 ` Nicolas Frattaroli
2026-07-29 6:19 ` [PATCH 3/3] drm/panthor: Add tracepoints for cache flushing Nicolas Frattaroli
2026-07-29 17:10 ` Steven Rostedt
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.