On Wed, Jun 24, 2026 at 11:23:35PM +0530, Sailesh Nandanavanam wrote:
> DTB co-firmware was previously requested and loaded in
> qcom_pas_load(), but its lifetime did not match the actual
> start/stop lifecycle of the remoteproc. As a result, the firmware
> reference could be retained across restart cycles, leading to a
> leak for each successful boot.
> 
> Additionally, if qcom_pas_start() failed after loading the DTB
> firmware, the remoteproc core would not invoke .stop(), leaving
> no opportunity to release the associated firmware reference.
> 
> Fix this by moving DTB firmware request and loading into
> qcom_pas_start(), so that its lifetime is strictly tied to the
> remoteproc start sequence.
> 
> Update qcom_pas_start() to ensure proper cleanup on all paths:
> - release PAS metadata on failure
> - release DTB firmware on both success and failure paths
> - unmap DTB carveout where applicable
> 
> Remove DTB firmware handling from qcom_pas_load(), as it does not
> match the correct ownership and lifecycle model.
> 
> With this change, request_firmware() and release_firmware() are
> properly paired within the start path, avoiding leaks and ensuring
> consistent behavior across restart and failure scenarios.
> 
> Fixes: 29814986b82e ("remoteproc: qcom_q6v5_pas: add support for dtb 
> co-firmware loading")
> Signed-off-by: Sailesh Nandanavanam <[email protected]>

Revision of the patch should be sent separately and not as reply
of the earlier revision.


> ---
> v2:
> - Move DTB firmware request/load from qcom_pas_load() to qcom_pas_start()
> - Fix firmware reference leak across restart cycles
> - Handle start() failure paths where .stop() is not invoked
> - Ensure firmware is released on all success and failure paths
> - Remove DTB handling from load() and drop release from stop()
> ---
>  drivers/remoteproc/qcom_q6v5_pas.c | 54 ++++++++++++++++--------------
>  1 file changed, 29 insertions(+), 25 deletions(-)
> 
> diff --git a/drivers/remoteproc/qcom_q6v5_pas.c 
> b/drivers/remoteproc/qcom_q6v5_pas.c
> index da27d1d3c9da..090f1f09dba3 100644
> --- a/drivers/remoteproc/qcom_q6v5_pas.c
> +++ b/drivers/remoteproc/qcom_q6v5_pas.c
> @@ -232,28 +232,7 @@ static int qcom_pas_load(struct rproc *rproc, const 
> struct firmware *fw)
>       if (pas->lite_dtb_pas_id)
>               qcom_scm_pas_shutdown(pas->lite_dtb_pas_id);
>  
> -     if (pas->dtb_pas_id) {
> -             ret = request_firmware(&pas->dtb_firmware, 
> pas->dtb_firmware_name, pas->dev);
> -             if (ret) {
> -                     dev_err(pas->dev, "request_firmware failed for %s: 
> %d\n",
> -                             pas->dtb_firmware_name, ret);
> -                     return ret;
> -             }
> -
> -             ret = qcom_mdt_pas_load(pas->dtb_pas_ctx, pas->dtb_firmware,
> -                                     pas->dtb_firmware_name, 
> pas->dtb_mem_region,
> -                                     &pas->dtb_mem_reloc);
> -             if (ret)
> -                     goto release_dtb_metadata;
> -     }
> -
>       return 0;
> -
> -release_dtb_metadata:
> -     qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
> -     release_firmware(pas->dtb_firmware);
> -
> -     return ret;
>  }
>  
>  static void qcom_pas_unmap_carveout(struct rproc *rproc, phys_addr_t 
> mem_phys, size_t size)
> @@ -277,9 +256,24 @@ static int qcom_pas_start(struct rproc *rproc)
>       struct qcom_pas *pas = rproc->priv;
>       int ret;
>  
> +     if (pas->dtb_pas_id) {
> +             ret = request_firmware(&pas->dtb_firmware, 
> pas->dtb_firmware_name, pas->dev);
> +             if (ret) {
> +                     dev_err(pas->dev, "request_firmware failed for %s: 
> %d\n",
> +                                     pas->dtb_firmware_name, ret);

Check the align of the line.


> +                     return ret;
> +             }
> +
> +             ret = qcom_mdt_pas_load(pas->dtb_pas_ctx, pas->dtb_firmware,
> +                             pas->dtb_firmware_name, pas->dtb_mem_region,
> +                             &pas->dtb_mem_reloc);

Here, as well.

> +             if (ret)
> +                     goto release_dtb_metadata;
> +     }
> +
>       ret = qcom_q6v5_prepare(&pas->q6v5);
>       if (ret)
> -             return ret;
> +             goto release_dtb_metadata;
>  
>       ret = qcom_pas_pds_enable(pas, pas->proxy_pds, pas->proxy_pd_count);
>       if (ret < 0)
> @@ -350,15 +344,17 @@ static int qcom_pas_start(struct rproc *rproc)
>       /* firmware is used to pass reference from qcom_pas_start(), drop it 
> now */
>       pas->firmware = NULL;
>  
> +     if (pas->dtb_firmware) {
> +             release_firmware(pas->dtb_firmware);
> +             pas->dtb_firmware = NULL;
> +     }

you can put this under check pas->dtb_pas_id which is already
there.

> +
>       return 0;
>  
>  unmap_carveout:
>       qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
>  release_pas_metadata:
>       qcom_scm_pas_metadata_release(pas->pas_ctx);
> -     if (pas->dtb_pas_id)
> -             qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
> -
>  unmap_dtb_carveout:
>       if (pas->dtb_pas_id)
>               qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, 
> pas->dtb_mem_size);
> @@ -376,6 +372,14 @@ static int qcom_pas_start(struct rproc *rproc)
>       qcom_pas_pds_disable(pas, pas->proxy_pds, pas->proxy_pd_count);
>  disable_irqs:
>       qcom_q6v5_unprepare(&pas->q6v5);
> +release_dtb_metadata:
> +     if (pas->dtb_pas_id)
> +             qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
> +release_dtb_firmware:

who is the user of this label ?


> +     if (pas->dtb_firmware) {

dtb_pas_id check should be fine here..


> +             release_firmware(pas->dtb_firmware);
> +             pas->dtb_firmware = NULL;
> +     }
>  
>       /* firmware is used to pass reference from qcom_pas_start(), drop it 
> now */
>       pas->firmware = NULL;
> -- 
> 2.34.1
> 

-- 
-Mukesh Ojha

Reply via email to