When qcom_pas_attach() finds the fatal SMP2P bit already set, it reports
a crash and then fails the attach. The core unwinds the attach while the
crash work is queued: rproc_boot() drops the power refcount back to 0,
and __rproc_attach()/rproc_attach() unprepare the subdevices, clean up
the resource table and disable the IOMMU. Once rproc_boot() releases
rproc->lock, the crash handler finds the rproc still RPROC_DETACHED,
marks it RPROC_CRASHED and runs rproc_boot_recovery() on top of that
torn-down state:
- the subdevices are unprepared a second time by rproc_stop(), so SSR
and sysmon notifiers see a duplicate shutdown;
- qcom_pas_stop() and the subsequent rproc_start() operate on resources
that rproc_attach() already released, e.g. iommu_unmap() on a
disabled domain for rproc->has_iommu;
- recovery leaves the rproc RPROC_RUNNING with power == 0. A later
"stop" via sysfs takes power to -1 and returns success without
stopping anything, so the remote stays running. A following "start"
then boots the firmware again on the running remote and fails to
re-add the still registered subdevices:
sysfs: cannot create duplicate filename '.../qcom_common.pd-mapper.0'
remoteproc remoteproc0: failed to prepare subdevices for adsp: -17
remoteproc remoteproc0: Boot failed: -17
A crash found at attach time is no different from one reported by the
fatal interrupt right after attaching. Factor the crash handling out of
q6v5_fatal_interrupt() into qcom_q6v5_report_fatal(), which logs the
SMEM crash reason, clears q6v5.running and reports the crash, and call
it from qcom_pas_attach() before letting the attach succeed. The core
completes the attach with balanced bookkeeping, and the crash handler
then recovers the subsystem through the regular RPROC_ATTACHED ->
RPROC_CRASHED path. With running cleared, qcom_q6v5_request_stop() does
not wait for a stop ack the dead remote cannot send, and handover_issued
already prevents a spurious handover on stop.
Assisted-by: LLM
Fixes: 16472c99f469 ("remoteproc: qcom: pas: Add late attach support for
subsystems")
Reviewed-by: Stephan Gerhold <[email protected]>
Signed-off-by: Shawn Guo <[email protected]>
---
drivers/remoteproc/qcom_q6v5.c | 22 +++++++++++++++++-----
drivers/remoteproc/qcom_q6v5.h | 1 +
drivers/remoteproc/qcom_q6v5_pas.c | 9 ++++++---
3 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c
index fe148b4b3775..51b24cbd4336 100644
--- a/drivers/remoteproc/qcom_q6v5.c
+++ b/drivers/remoteproc/qcom_q6v5.c
@@ -147,15 +147,18 @@ static irqreturn_t q6v5_wdog_interrupt(int irq, void
*data)
return IRQ_HANDLED;
}
-static irqreturn_t q6v5_fatal_interrupt(int irq, void *data)
+/**
+ * qcom_q6v5_report_fatal() - report a fatal error of the remote processor
+ * @q6v5: reference to qcom_q6v5 context
+ *
+ * Log the crash reason published by the remote in SMEM, mark the remote as
+ * no longer running and report the crash to the remoteproc core.
+ */
+void qcom_q6v5_report_fatal(struct qcom_q6v5 *q6v5)
{
- struct qcom_q6v5 *q6v5 = data;
size_t len;
char *msg;
- if (!q6v5->running)
- return IRQ_HANDLED;
-
msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, q6v5->crash_reason, &len);
if (!IS_ERR(msg) && len > 0 && msg[0])
dev_err(q6v5->dev, "fatal error received: %s\n", msg);
@@ -164,6 +167,15 @@ static irqreturn_t q6v5_fatal_interrupt(int irq, void
*data)
q6v5->running = false;
rproc_report_crash(q6v5->rproc, RPROC_FATAL_ERROR);
+}
+EXPORT_SYMBOL_GPL(qcom_q6v5_report_fatal);
+
+static irqreturn_t q6v5_fatal_interrupt(int irq, void *data)
+{
+ struct qcom_q6v5 *q6v5 = data;
+
+ if (q6v5->running)
+ qcom_q6v5_report_fatal(q6v5);
return IRQ_HANDLED;
}
diff --git a/drivers/remoteproc/qcom_q6v5.h b/drivers/remoteproc/qcom_q6v5.h
index 8991ff090579..a0507860a21c 100644
--- a/drivers/remoteproc/qcom_q6v5.h
+++ b/drivers/remoteproc/qcom_q6v5.h
@@ -56,5 +56,6 @@ int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5);
int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon);
int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout);
unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5);
+void qcom_q6v5_report_fatal(struct qcom_q6v5 *q6v5);
#endif
diff --git a/drivers/remoteproc/qcom_q6v5_pas.c
b/drivers/remoteproc/qcom_q6v5_pas.c
index 89450cbdb1ee..c50744bee506 100644
--- a/drivers/remoteproc/qcom_q6v5_pas.c
+++ b/drivers/remoteproc/qcom_q6v5_pas.c
@@ -562,10 +562,13 @@ static int qcom_pas_attach(struct rproc *rproc)
goto disable_running;
if (crash_state) {
+ /*
+ * Complete the attach and let the crash handler recover the
+ * subsystem from the attached state, as for a crash reported
+ * by the fatal interrupt.
+ */
dev_err(pas->dev, "Subsystem has crashed before driver
probe\n");
- rproc_report_crash(rproc, RPROC_FATAL_ERROR);
- ret = -EINVAL;
- goto disable_running;
+ qcom_q6v5_report_fatal(&pas->q6v5);
}
return 0;
--
2.43.0