summaryrefslogtreecommitdiff
path: root/tools/perf/scripts/python/stackcollapse.py
diff options
context:
space:
mode:
authorVishwaroop A <va@nvidia.com>2026-08-13 20:00:26 +0000
committerMark Brown <broonie@kernel.org>2026-09-09 12:48:30 +0100
commit1ff82c13ea9475abc51eebfb83ee54bc75c08900 (patch)
treeb3760ded16d46ad021117a48c25930652d12200c /tools/perf/scripts/python/stackcollapse.py
parente329b94b1e47d1a73706bd09380bf0989fbcd4a8 (diff)
spi: tegra210-quad: Cache TRANS_STATUS in ISR for timeout handler
On heavily loaded systems the workqueue bottom half can be delayed long enough for wait_for_completion_timeout() to expire before the ISR's queued work actually runs. Reading QSPI_TRANS_STATUS directly from the controller in the timeout handler races with both the workqueue handler and the controller itself, and can mis-classify a transfer that genuinely timed out as having "completed". Cache the controller status captured by the hard IRQ before it is acked, and let the timeout handler consume that cache: - tegra_qspi_isr() reads QSPI_FIFO_STATUS and QSPI_TRANS_STATUS, derives tx_status / rx_status, publishes them via WRITE_ONCE(), and then publishes the trans_status cache via smp_store_release() *before* masking and acking the controller IRQ. Publish-before-clear is required so that a timeout handler that fell back to a live QSPI_TRANS_STATUS read (because it saw the cache still zero on another CPU) also sees the hardware RDY bit that has not been cleared yet. - tegra_qspi_handle_timeout() consumes trans_status with a paired smp_load_acquire() and a cache-live-cache retry pattern. If the initial cache load returns zero, the handler reads the live QSPI_TRANS_STATUS register; if that also returns zero it retries the cache once more. That closes the interleaving where an ISR publishes trans_status with release semantics and then W1Cs the hardware between the timeout handler's cache load and its live load, otherwise leaving the timeout handler with cache = 0 and HW = 0 (a false timeout on a transfer that has in fact just completed). - tegra_qspi_setup_transfer_one() and both tegra_qspi_start_{cpu,dma}_based_transfer() paths clear the cache with smp_store_release() under the spinlock before unmasking the IRQ for the new chunk, so a stale RDY bit from a previous chunk of a multi-chunk transfer cannot fool the handler. Serialise handle_timeout with the workqueue and the ISR unconditionally. Every expired wait_for_completion_timeout() enters the recovery state: publish recovery_in_progress under tqspi->lock, mask the controller IRQ, synchronize_irq() to drain any in-flight hard IRQ (including the small-PIO fastpath), and cancel_work_sync() to drain the workqueue. This holds regardless of whether the hardware finished, because a genuine hardware timeout still races the caller's dma_stop() + device_reset() + curr_xfer clear against a delayed ISR or worker that arrives immediately after the status sample. Classifying the timeout as -ETIMEDOUT only *after* serialisation gives the caller a stable state to clean up. Snapshot the live FIFO error status *before* entering recovery when the ISR cache is empty and the live QSPI_TRANS_STATUS shows RDY (the lost-IRQ path). tegra_qspi_mask_clear_irq() W1Cs QSPI_TRANS_STATUS and the QSPI_FIFO_STATUS error bits, so a lost-IRQ recovery that called it first would erase the very error state the manual handler downstream needs to see. Capturing the snapshot before the mask and publishing it into tqspi->{status_reg,tx_status,rx_status} after the drain keeps the manual final-chunk handler operating on fresh error data rather than stale fields from an earlier ISR run. cancel_work_sync() cancels a pending worker without executing it and waits for a currently running one to finish. The recovery_in_progress guard is checked inside tegra_qspi_isr() under the same tqspi->lock as its queue_work() and small-PIO fastpath dispatch decisions, so no new bottom-half work is enqueued once we publish the flag. synchronize_irq() closes the window where an ISR observed recovery_in_progress == false, released the lock, and is about to call queue_work(): we wait for that ISR to finish before draining the workqueue, so its queued work is caught by cancel_work_sync(). After the drain, re-check the cache once more (the drained worker may have published a completion status the entry snapshot did not observe). If try_wait_for_completion() reports the whole transfer completed, return success. Restrict the manual fallback that invokes handle_{cpu,dma}_based_xfer() from process context to the last chunk of a transfer. On an intermediate chunk of a multi-chunk DMA transfer the work handler may have processed the current chunk and armed the next chunk (unmasked the IRQ and kicked HW) before cancel_work_sync() returned; running another handler here would let the caller's seq_xfer clear curr_xfer and finalise the message while the DMA engine is still moving the next chunk into the client buffer. Return -ETIMEDOUT in that case and let the caller's existing dma_stop() + reset() path clean up. Before returning, re-mask the controller IRQ and synchronize_irq() one more time. The drained bottom half may have unmasked the IRQ when arming a subsequent chunk; without the re-mask a lingering RDY IRQ that arrives after this function returns could invoke the ISR and queue a worker after recovery_in_progress has been cleared, racing the caller's cleanup of curr_xfer. Signed-off-by: Vishwaroop A <va@nvidia.com> Link: https://patch.msgid.link/20260813200027.2711863-3-va@nvidia.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'tools/perf/scripts/python/stackcollapse.py')
0 files changed, 0 insertions, 0 deletions