[PATCH 2/4] remoteproc: virtio: Rework memory release
From: Francesco Valla
Date: Sun Nov 08 2026 - 17:34:20 EST
The current memory release setup has two issues, which may happen in
specific corner cases:
- a failure in kzalloc_obj() during subdev.start() would keep alive
the association between the pdev and the memory setups done just
before, leaking them and preventing further start attempts;
- in case of a crash recovery restart, where the pdev is maintained
but subdev.start() is called again, a delayed release of the
previous vdev either makes the start fail with -EBUSY (dedicated
vdev buffer) or, when falling back to the parent's reserved memory,
makes the memory setup disappear underneath the newly created vdev.
Move the memory release calls to subdev.stop(), symmetrically to what
happens in subdev.start(), and add them to the failure path of the
start() itself. The get()/put() actions on the pdev, that are now
useless since no access to it is performed during the vdev release, can
be safely dropped.
Fixes: b327c72753d6 ("remoteproc: virtio: Fix wdg cannot recovery remote processor")
Signed-off-by: Francesco Valla <francesco@xxxxxxxx>
---
drivers/remoteproc/remoteproc_virtio.c | 32 +++++++++++++++-----------------
1 file changed, 15 insertions(+), 17 deletions(-)
diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c
index 7979b2570998..bd4edc89e696 100644
--- a/drivers/remoteproc/remoteproc_virtio.c
+++ b/drivers/remoteproc/remoteproc_virtio.c
@@ -344,8 +344,7 @@ static const struct virtio_config_ops rproc_virtio_config_ops = {
/*
* This function is called whenever vdev is released, and is responsible
- * to decrement the platform device's refcount which was taken when vdev was
- * added.
+ * to free the memory allocated for the vdev.
*
* Never call this function directly; it will be called by the driver
* core when needed.
@@ -354,11 +353,6 @@ static void rproc_virtio_dev_release(struct device *dev)
{
struct virtio_device *vdev = dev_to_virtio(dev);
- of_reserved_mem_device_release(dev->parent);
- dma_release_coherent_memory(dev->parent);
-
- put_device(dev->parent);
-
kfree(vdev);
}
@@ -380,9 +374,8 @@ static int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
int ret;
if (rproc->ops->kick == NULL) {
- ret = -EINVAL;
dev_err(dev, ".kick method not defined for %s\n", rproc->name);
- goto out;
+ return -EINVAL;
}
/* Try to find dedicated vdev buffer carveout */
@@ -398,7 +391,7 @@ static int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
mem->of_resm_idx);
if (ret) {
dev_err(dev, "Can't associate reserved memory\n");
- goto out;
+ return ret;
}
} else {
if (mem->va) {
@@ -416,7 +409,7 @@ static int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
mem->len);
if (ret < 0) {
dev_err(dev, "Failed to associate buffer\n");
- goto out;
+ return ret;
}
}
} else {
@@ -437,26 +430,28 @@ static int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
vdev = kzalloc_obj(*vdev);
if (!vdev) {
ret = -ENOMEM;
- goto out;
+ goto release_mem;
}
vdev->id.device = id,
vdev->config = &rproc_virtio_config_ops,
vdev->dev.parent = dev;
vdev->dev.release = rproc_virtio_dev_release;
- /* Reference the parent device, which will be used during teardown. */
- get_device(dev);
-
ret = register_virtio_device(vdev);
if (ret) {
put_device(&vdev->dev);
dev_err(dev, "failed to register vdev: %d\n", ret);
- goto out;
+ goto release_mem;
}
dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id);
-out:
+ return 0;
+
+release_mem:
+ of_reserved_mem_device_release(dev);
+ dma_release_coherent_memory(dev);
+
return ret;
}
@@ -493,6 +488,9 @@ 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);
+
+ of_reserved_mem_device_release(dev);
+ dma_release_coherent_memory(dev);
}
static int rproc_virtio_probe(struct platform_device *pdev)
--
2.56.0