All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Shawn Guo" <shengchao.guo@oss.qualcomm.com>
Cc: devicetree@vger.kernel.org, conor+dt@kernel.org, robh@kernel.org
Subject: Re: [PATCH 7/8] remoteproc: qcom: pas: Enforce coupled stop/crash for HPASS ADSP clusters
Date: Sat, 15 Aug 2026 13:39:35 +0000	[thread overview]
Message-ID: <20260815133936.0502D1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260815132541.1575121-8-shengchao.guo@oss.qualcomm.com>

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [High] Canceling `pas->stop_work` before removing the member from `cluster->members` creates a TOCTOU race window that leads to a Use-After-Free.
- [High] Coordinated stop state machine permanently deadlocks if a participant has multiple power references due to the use of `rproc_shutdown()`.
- [High] Concurrent crashes bypass the coordination barrier and prematurely power off hardware, violating the cluster constraint.
- [High] Sibling auto-restart times out prematurely when the cluster root crashes, leaving siblings permanently offline.
- [High] Lockless iteration of `cluster->members` in `qcom_pas_cluster_cascade_work()` races with `qcom_pas_cluster_init()`, causing list corruption.
- [Medium] Concurrent crash of a cluster member inflates its `rproc->power` refcount during auto-restart, preventing it from being successfully stopped by the user.
--

--- Patch [7]: [PATCH 7/8] remoteproc: qcom: pas: Enforce coupled stop/crash for HPASS ADSP clusters ---
commit 6bd05de74159725dcc440502485f8bb8e3c952cb
Author: Shawn Guo <shengchao.guo@oss.qualcomm.com>

remoteproc: qcom: pas: Enforce coupled stop/crash for HPASS ADSP clusters

This commit enforces a coupled stop and crash mechanism for Nord HPASS ADSP
clusters. It orchestrates a two-phase stop and hardware power-off sequence
across cluster members to prevent sysmon timeouts and recovery failures. It
also implements an automatic root-first restart sequence upon a crash.

> diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
> index ef38a1929f56b..ac3b92c7e7e79 100644
> --- a/drivers/remoteproc/qcom_q6v5_pas.c
> +++ b/drivers/remoteproc/qcom_q6v5_pas.c

[ ... ]

> +static void qcom_pas_stop_work_fn(struct work_struct *work)
> +{
> +	struct qcom_pas *pas = container_of(work, struct qcom_pas, stop_work);
> +
> +	rproc_shutdown(pas->rproc);

[Severity: High]
If a sibling has an rproc->power refcount greater than 1 (e.g., booted by
multiple clients), rproc_shutdown() will simply decrement the refcount and
return without invoking qcom_pas_stop().

Since qcom_pas_cluster_stop_complete() would never be called for that
sibling, cluster->stop_done_pending will never reach 0.

Could this leave cluster->stop_in_progress permanently set to true and
deadlock the coordinated stop state machine for all future operations?

> +}
> +
> +/**
> + * qcom_pas_cluster_cascade_work() - restart a crashed cluster, root first
> + * @work:	the cluster's cascade_work

[ ... ]

> +static void qcom_pas_cluster_cascade_work(struct work_struct *work)
> +{
> +	struct qcom_pas_cluster *cluster = container_of(work, struct qcom_pas_cluster,
> +						       cascade_work);
> +	struct qcom_pas *pas, *origin, *root;
> +
> +	mutex_lock(&cluster->lock);
> +	origin = cluster->cascade_origin;
> +	root = cluster->root;
> +	mutex_unlock(&cluster->lock);
> +
> +	/*
> +	 * Membership is stable here: qcom_pas_cluster_exit() always
> +	 * cancel_work_sync()s this work before touching cluster->members, so no

[ ... ]

> +	if (root && root != origin) {
> +		int ret;
> +
> +		ret = rproc_boot(root->rproc);
> +		if (ret) {
> +			dev_err(root->dev, "failed to restart cluster root: %d\n", ret);
> +			return;
> +		}
> +	}
> +
> +	list_for_each_entry(pas, &cluster->members, cluster_node) {

[Severity: High]
Is it safe to iterate cluster->members locklessly here?

If a cluster crash recovery executes concurrently with the probe of a new
cluster member, qcom_pas_cluster_init() could call list_add_tail() while
holding cluster->lock.

Could traversing this list without holding cluster->lock result in list
corruption or infinite loops?

> +		int ret;
> +
> +		if (pas == origin || pas == root)
> +			continue;
> +
> +		ret = rproc_boot(pas->rproc);

[Severity: High]
If the cluster root crashes, its recovery thread will trigger a coordinated
stop and schedule this cascade_work. Siblings are immediately booted here,
which eventually calls qcom_pas_cluster_wait_for_root(), enforcing a 1-second
timeout waiting for the root to boot.

However, the root's recovery thread must still generate a coredump and load
firmware, which typically takes significantly longer than 1 second.

Will this cause the auto-restarting siblings to prematurely time out and
fail to boot?

[Severity: Medium]
When auto-restarting siblings, this loops through the entire cluster without
checking if the members actually participated in the coordinated stop.

If a sibling crashed concurrently and was excluded from the stop (its
stop_work didn't execute and its refcount wasn't decremented), unconditionally
calling rproc_boot() on it here will artificially inflate its rproc->power
refcount.

Could this permanent refcount leak prevent the remote processor from being
successfully shut down by the user later?

> +		if (ret)
> +			dev_err(pas->dev, "failed to restart cluster sibling: %d\n", ret);
> +	}
> +}

[ ... ]

> @@ -273,6 +364,9 @@ static void qcom_pas_cluster_exit(struct qcom_pas *pas)
>  	if (!cluster)
>  		return;
>  
> +	cancel_work_sync(&pas->stop_work);
> +	cancel_work_sync(&cluster->cascade_work);
> +
>  	mutex_lock(&cluster->lock);
>  	list_del(&pas->cluster_node);

[Severity: High]
Does qcom_pas_cluster_exit() have a race condition that could lead to a
use-after-free?

If a member is being removed, cancel_work_sync() is called while the member
is still in the cluster->members list. If a sibling concurrently triggers
a cluster stop, it can find this member in the list and schedule
pas->stop_work via qcom_pas_cluster_trigger_stop():

    list_for_each_entry(member, &cluster->members, cluster_node) {
        if (member == pas || !member->in_cluster_stop)
            continue;
        schedule_work(&member->stop_work);
    }

Could this result in the work executing on a freed pas structure after
device removal completes?

[ ... ]

> @@ -625,10 +866,18 @@ static int qcom_pas_stop(struct rproc *rproc)
>  		mutex_unlock(&pas->cluster->lock);
>  	}
>  
> +	if (pas->cluster)
> +		qcom_pas_cluster_trigger_stop(pas, rproc->state == RPROC_CRASHED);
> +
> +	/* Phase 1: request and await this member's own graceful ack */
>  	ret = qcom_q6v5_request_stop(&pas->q6v5, pas->sysmon);
>  	if (ret == -ETIMEDOUT)
>  		dev_err(pas->dev, "timed out on wait\n");
>  
> +	if (pas->cluster)
> +		qcom_pas_cluster_stop_barrier(pas);
> +
> +	/* Phase 2: the whole cluster has acked, power the hardware off */
>  	ret = qcom_pas_shutdown(pas->pas_id);

[Severity: High]
If a sibling crashes concurrently with the original stopping member,
qcom_pas_cluster_member_stops() returns false for it, excluding it from
the coordinated stop.

When that sibling's own recovery thread later calls qcom_pas_stop(), it sees
stop_in_progress is true but its in_cluster_stop is false. This causes it
to bypass qcom_pas_cluster_stop_barrier() and immediately call
qcom_pas_shutdown().

Doesn't this power off its hardware instantly while other participants are
still executing Phase 1, violating the constraint that no member shuts down
before the whole cluster has acked?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260815132541.1575121-1-shengchao.guo@oss.qualcomm.com?part=7

  reply	other threads:[~2026-08-15 13:39 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-15 13:25 [PATCH 0/8] remoteproc: qcom: Support the Nord HPASS ADSP cluster Shawn Guo
2026-08-15 13:25 ` [PATCH 1/8] dt-bindings: remoteproc: qcom,nord-pas: Add qcom,cluster-root property Shawn Guo
2026-08-15 13:35   ` sashiko-bot
2026-08-15 13:25 ` [PATCH 2/8] remoteproc: Add cluster field to struct rproc Shawn Guo
2026-08-15 13:25 ` [PATCH 3/8] remoteproc: qcom: sysmon: Suppress notify between cluster siblings Shawn Guo
2026-08-15 13:33   ` sashiko-bot
2026-08-15 13:25 ` [PATCH 4/8] rpmsg: qcom_glink_ssr: Suppress cleanup " Shawn Guo
2026-08-15 13:36   ` sashiko-bot
2026-08-15 13:25 ` [PATCH 5/8] remoteproc: qcom: pas: Track HPASS ADSP cluster membership Shawn Guo
2026-08-15 13:25 ` [PATCH 6/8] remoteproc: qcom: pas: Sequence HPASS ADSP cluster boot root-first Shawn Guo
2026-08-15 13:34   ` sashiko-bot
2026-08-15 13:25 ` [PATCH 7/8] remoteproc: qcom: pas: Enforce coupled stop/crash for HPASS ADSP clusters Shawn Guo
2026-08-15 13:39   ` sashiko-bot [this message]
2026-08-15 13:25 ` [PATCH 8/8] remoteproc: qcom: pas: Add Nord ADSP1/2 support Shawn Guo

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=20260815133936.0502D1F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=shengchao.guo@oss.qualcomm.com \
    /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.