* [PATCH 1/3] remoteproc: check return value of subdev stop and unprepare callbacks
2026-06-09 10:22 [PATCH 0/3] remoteproc: fix silent teardown failures and prevent SMMU faults on hung DSP Mukesh Ojha
@ 2026-06-09 10:22 ` Mukesh Ojha
2026-06-09 10:22 ` [PATCH 2/3] remoteproc: abort subdev stop sequence on first failure Mukesh Ojha
2026-06-09 10:22 ` [PATCH 3/3] remoteproc: qcom_sysmon: abort stop on unacknowledged shutdown Mukesh Ojha
2 siblings, 0 replies; 5+ messages in thread
From: Mukesh Ojha @ 2026-06-09 10:22 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Matthias Brugger,
AngeloGioacchino Del Regno
Cc: linux-arm-msm, linux-remoteproc, linux-kernel, linux-arm-kernel,
linux-mediatek, Mukesh Ojha
The stop() and unprepare() callbacks in struct rproc_subdev were void,
making it impossible for implementations to report failures. Unlike
prepare() and start() which already return int and have their errors
checked, errors during teardown were silently discarded.
Change the callback signatures to return int. Update
rproc_stop_subdevices() and rproc_unprepare_subdevices() to check each
return value and emit a warning on failure while continuing to visit all
remaining subdevices (best-effort teardown).
rproc_vdev_do_stop() propagates the error from device_for_each_child()
which it was already computing but had no way to surface. All other
implementations (glink, smd, ssr, pdm, sysmon) gain a return 0 as they
have no failure paths.
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
---
drivers/remoteproc/qcom_common.c | 26 +++++++++++++++++++-------
drivers/remoteproc/qcom_sysmon.c | 10 +++++++---
drivers/remoteproc/remoteproc_core.c | 16 ++++++++++++----
drivers/remoteproc/remoteproc_virtio.c | 4 +++-
drivers/rpmsg/mtk_rpmsg.c | 8 ++++++--
include/linux/remoteproc.h | 4 ++--
6 files changed, 49 insertions(+), 19 deletions(-)
diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c
index fd2b6824ad26..05a599318763 100644
--- a/drivers/remoteproc/qcom_common.c
+++ b/drivers/remoteproc/qcom_common.c
@@ -216,19 +216,23 @@ static int glink_subdev_start(struct rproc_subdev *subdev)
return PTR_ERR_OR_ZERO(glink->edge);
}
-static void glink_subdev_stop(struct rproc_subdev *subdev, bool crashed)
+static int glink_subdev_stop(struct rproc_subdev *subdev, bool crashed)
{
struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
qcom_glink_smem_unregister(glink->edge);
glink->edge = NULL;
+
+ return 0;
}
-static void glink_subdev_unprepare(struct rproc_subdev *subdev)
+static int glink_subdev_unprepare(struct rproc_subdev *subdev)
{
struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
qcom_glink_ssr_notify(glink->ssr_name);
+
+ return 0;
}
/**
@@ -327,12 +331,14 @@ static int smd_subdev_start(struct rproc_subdev *subdev)
return PTR_ERR_OR_ZERO(smd->edge);
}
-static void smd_subdev_stop(struct rproc_subdev *subdev, bool crashed)
+static int smd_subdev_stop(struct rproc_subdev *subdev, bool crashed)
{
struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
qcom_smd_unregister_edge(smd->edge);
smd->edge = NULL;
+
+ return 0;
}
/**
@@ -465,7 +471,7 @@ static int ssr_notify_start(struct rproc_subdev *subdev)
return 0;
}
-static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed)
+static int ssr_notify_stop(struct rproc_subdev *subdev, bool crashed)
{
struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
struct qcom_ssr_notify_data data = {
@@ -475,9 +481,11 @@ static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed)
srcu_notifier_call_chain(&ssr->info->notifier_list,
QCOM_SSR_BEFORE_SHUTDOWN, &data);
+
+ return 0;
}
-static void ssr_notify_unprepare(struct rproc_subdev *subdev)
+static int ssr_notify_unprepare(struct rproc_subdev *subdev)
{
struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
struct qcom_ssr_notify_data data = {
@@ -487,6 +495,8 @@ static void ssr_notify_unprepare(struct rproc_subdev *subdev)
srcu_notifier_call_chain(&ssr->info->notifier_list,
QCOM_SSR_AFTER_SHUTDOWN, &data);
+
+ return 0;
}
/**
@@ -572,16 +582,18 @@ static int pdm_notify_prepare(struct rproc_subdev *subdev)
}
-static void pdm_notify_unprepare(struct rproc_subdev *subdev)
+static int pdm_notify_unprepare(struct rproc_subdev *subdev)
{
struct qcom_rproc_pdm *pdm = to_pdm_subdev(subdev);
if (!pdm->adev)
- return;
+ return 0;
auxiliary_device_delete(pdm->adev);
auxiliary_device_uninit(pdm->adev);
pdm->adev = NULL;
+
+ return 0;
}
/**
diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c
index 913e3b750a86..44b905a7e129 100644
--- a/drivers/remoteproc/qcom_sysmon.c
+++ b/drivers/remoteproc/qcom_sysmon.c
@@ -531,7 +531,7 @@ static int sysmon_start(struct rproc_subdev *subdev)
return 0;
}
-static void sysmon_stop(struct rproc_subdev *subdev, bool crashed)
+static int sysmon_stop(struct rproc_subdev *subdev, bool crashed)
{
struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev);
struct sysmon_event event = {
@@ -548,7 +548,7 @@ static void sysmon_stop(struct rproc_subdev *subdev, bool crashed)
/* Don't request graceful shutdown if we've crashed */
if (crashed)
- return;
+ return 0;
if (sysmon->ssctl_instance) {
if (!wait_for_completion_timeout(&sysmon->ssctl_comp, HZ / 2))
@@ -559,9 +559,11 @@ static void sysmon_stop(struct rproc_subdev *subdev, bool crashed)
sysmon->shutdown_acked = ssctl_request_shutdown(sysmon);
else if (sysmon->ept)
sysmon->shutdown_acked = sysmon_request_shutdown(sysmon);
+
+ return 0;
}
-static void sysmon_unprepare(struct rproc_subdev *subdev)
+static int sysmon_unprepare(struct rproc_subdev *subdev)
{
struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon,
subdev);
@@ -574,6 +576,8 @@ static void sysmon_unprepare(struct rproc_subdev *subdev)
sysmon->state = SSCTL_SSR_EVENT_AFTER_SHUTDOWN;
blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event);
mutex_unlock(&sysmon->state_lock);
+
+ return 0;
}
/**
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index f003be006b1b..21127d972bff 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -1113,20 +1113,28 @@ static int rproc_start_subdevices(struct rproc *rproc)
static void rproc_stop_subdevices(struct rproc *rproc, bool crashed)
{
struct rproc_subdev *subdev;
+ int ret;
list_for_each_entry_reverse(subdev, &rproc->subdevs, node) {
- if (subdev->stop)
- subdev->stop(subdev, crashed);
+ if (subdev->stop) {
+ ret = subdev->stop(subdev, crashed);
+ if (ret)
+ dev_warn(&rproc->dev, "subdev stop failed: %d\n", ret);
+ }
}
}
static void rproc_unprepare_subdevices(struct rproc *rproc)
{
struct rproc_subdev *subdev;
+ int ret;
list_for_each_entry_reverse(subdev, &rproc->subdevs, node) {
- if (subdev->unprepare)
- subdev->unprepare(subdev);
+ if (subdev->unprepare) {
+ ret = subdev->unprepare(subdev);
+ if (ret)
+ dev_warn(&rproc->dev, "subdev unprepare failed: %d\n", ret);
+ }
}
}
diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c
index d5e9ff045a28..128d3088a959 100644
--- a/drivers/remoteproc/remoteproc_virtio.c
+++ b/drivers/remoteproc/remoteproc_virtio.c
@@ -480,7 +480,7 @@ static int rproc_vdev_do_start(struct rproc_subdev *subdev)
return rproc_add_virtio_dev(rvdev, rvdev->id);
}
-static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed)
+static int rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed)
{
struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev);
struct device *dev = &rvdev->pdev->dev;
@@ -489,6 +489,8 @@ static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed)
ret = device_for_each_child(dev, NULL, rproc_remove_virtio_dev);
if (ret)
dev_warn(dev, "can't remove vdev child device: %d\n", ret);
+
+ return ret;
}
static int rproc_virtio_probe(struct platform_device *pdev)
diff --git a/drivers/rpmsg/mtk_rpmsg.c b/drivers/rpmsg/mtk_rpmsg.c
index 1b670ed54cfa..d8ea77055f31 100644
--- a/drivers/rpmsg/mtk_rpmsg.c
+++ b/drivers/rpmsg/mtk_rpmsg.c
@@ -326,7 +326,7 @@ static int mtk_rpmsg_prepare(struct rproc_subdev *subdev)
return 0;
}
-static void mtk_rpmsg_unprepare(struct rproc_subdev *subdev)
+static int mtk_rpmsg_unprepare(struct rproc_subdev *subdev)
{
struct mtk_rpmsg_rproc_subdev *mtk_subdev = to_mtk_subdev(subdev);
@@ -334,9 +334,11 @@ static void mtk_rpmsg_unprepare(struct rproc_subdev *subdev)
mtk_rpmsg_destroy_ept(mtk_subdev->ns_ept);
mtk_subdev->ns_ept = NULL;
}
+
+ return 0;
}
-static void mtk_rpmsg_stop(struct rproc_subdev *subdev, bool crashed)
+static int mtk_rpmsg_stop(struct rproc_subdev *subdev, bool crashed)
{
struct mtk_rpmsg_channel_info *info, *next;
struct mtk_rpmsg_rproc_subdev *mtk_subdev = to_mtk_subdev(subdev);
@@ -372,6 +374,8 @@ static void mtk_rpmsg_stop(struct rproc_subdev *subdev, bool crashed)
kfree(info);
}
mutex_unlock(&mtk_subdev->channels_lock);
+
+ return 0;
}
struct rproc_subdev *
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index 7c1546d48008..315c479d163a 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -335,8 +335,8 @@ struct rproc_subdev {
int (*prepare)(struct rproc_subdev *subdev);
int (*start)(struct rproc_subdev *subdev);
- void (*stop)(struct rproc_subdev *subdev, bool crashed);
- void (*unprepare)(struct rproc_subdev *subdev);
+ int (*stop)(struct rproc_subdev *subdev, bool crashed);
+ int (*unprepare)(struct rproc_subdev *subdev);
};
/* we currently support only two vrings per rvdev */
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 2/3] remoteproc: abort subdev stop sequence on first failure
2026-06-09 10:22 [PATCH 0/3] remoteproc: fix silent teardown failures and prevent SMMU faults on hung DSP Mukesh Ojha
2026-06-09 10:22 ` [PATCH 1/3] remoteproc: check return value of subdev stop and unprepare callbacks Mukesh Ojha
@ 2026-06-09 10:22 ` Mukesh Ojha
2026-06-09 11:43 ` Stephan Gerhold
2026-06-09 10:22 ` [PATCH 3/3] remoteproc: qcom_sysmon: abort stop on unacknowledged shutdown Mukesh Ojha
2 siblings, 1 reply; 5+ messages in thread
From: Mukesh Ojha @ 2026-06-09 10:22 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Matthias Brugger,
AngeloGioacchino Del Regno
Cc: linux-arm-msm, linux-remoteproc, linux-kernel, linux-arm-kernel,
linux-mediatek, Mukesh Ojha
If a subdevice fails to stop, it indicates broken communication with the
DSP. Continuing to stop further subdevices against an unresponsive
remote processor could close rpmsg devices that could remove the memory
mapping from HLOS and in case if remote processor touches those memory
can result in SMMU fault.
Change rproc_stop_subdevices() to return int and abort on the first
failing subdev. Propagate the error through rproc_stop() and
__rproc_detach() so callers are aware the teardown did not complete
cleanly.
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
---
drivers/remoteproc/remoteproc_core.c | 24 +++++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 21127d972bff..77c4f09c7604 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -1110,7 +1110,7 @@ static int rproc_start_subdevices(struct rproc *rproc)
return ret;
}
-static void rproc_stop_subdevices(struct rproc *rproc, bool crashed)
+static int rproc_stop_subdevices(struct rproc *rproc, bool crashed)
{
struct rproc_subdev *subdev;
int ret;
@@ -1118,10 +1118,14 @@ static void rproc_stop_subdevices(struct rproc *rproc, bool crashed)
list_for_each_entry_reverse(subdev, &rproc->subdevs, node) {
if (subdev->stop) {
ret = subdev->stop(subdev, crashed);
- if (ret)
- dev_warn(&rproc->dev, "subdev stop failed: %d\n", ret);
+ if (ret) {
+ dev_err(&rproc->dev, "subdev stop failed: %d\n", ret);
+ return ret;
+ }
}
}
+
+ return 0;
}
static void rproc_unprepare_subdevices(struct rproc *rproc)
@@ -1712,7 +1716,12 @@ static int rproc_stop(struct rproc *rproc, bool crashed)
return -EINVAL;
/* Stop any subdevices for the remote processor */
- rproc_stop_subdevices(rproc, crashed);
+ ret = rproc_stop_subdevices(rproc, crashed);
+ if (ret) {
+ dev_err(dev, "failed to stop subdevices for %s: %d\n",
+ rproc->name, ret);
+ return ret;
+ }
/* the installed resource table is no longer accessible */
ret = rproc_reset_rsc_table_on_stop(rproc);
@@ -1751,7 +1760,12 @@ static int __rproc_detach(struct rproc *rproc)
return -EINVAL;
/* Stop any subdevices for the remote processor */
- rproc_stop_subdevices(rproc, false);
+ ret = rproc_stop_subdevices(rproc, false);
+ if (ret) {
+ dev_err(dev, "failed to stop subdevices for %s: %d\n",
+ rproc->name, ret);
+ return ret;
+ }
/* the installed resource table is no longer accessible */
ret = rproc_reset_rsc_table_on_detach(rproc);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread