[PATCH v3 1/2] media: atomisp: fix memory leak in atomisp_pci_probe()

From: Dawei Feng

Date: Tue Jun 16 2026 - 09:50:24 EST


atomisp_initialize_modules() creates CSI2 and ISP subdev media entities
before atomisp_pci_probe() registers them. Its counterpart,
atomisp_uninitialize_modules(), only releases part of that module-owned
state and leaves some media entity cleanup to the entity unregister path.

That ownership split is incomplete for probe error paths. If
atomisp_pci_probe() fails after module initialization but before all
entities are registered, the unwind path cannot rely on unregister
helpers to release media entity state whose lifetime started in module
initialization. The CSI2 and ISP subdev media entities can therefore be
left allocated.

Refactor the cleanup boundary so module cleanup releases media entities
created by module initialization, while unregister helpers only undo
registered V4L2 and media device state. Move CSI2 and ISP subdev media
entity cleanup into atomisp_mipi_csi2_cleanup() and the new
atomisp_subdev_cleanup(), and run media_device_cleanup() after module
cleanup in the probe unwind and remove paths.

If atomisp_mipi_csi2_init() itself fails, it has already unwound its
partial setup, so return the error directly. Only the later
atomisp_subdev_init() failure path needs to clean up CSI2 from the
caller.

Fixes: 9d4fa1a16b28 ("media: atomisp: cleanup directory hierarchy")
Reported-by: Zilin Guan <zilin@xxxxxxxxxx>
Signed-off-by: Dawei Feng <dawei.feng@xxxxxxxxxx>
---
drivers/staging/media/atomisp/pci/atomisp_csi2.c | 5 ++++-
drivers/staging/media/atomisp/pci/atomisp_subdev.c | 9 +++++++--
drivers/staging/media/atomisp/pci/atomisp_v4l2.c | 12 +++++-------
3 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.c b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
index 95b9113d75e9..2a85d04ade81 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
@@ -185,7 +185,6 @@ static int mipi_csi2_init_entities(struct atomisp_mipi_csi2_device *csi2,
void
atomisp_mipi_csi2_unregister_entities(struct atomisp_mipi_csi2_device *csi2)
{
- media_entity_cleanup(&csi2->subdev.entity);
v4l2_device_unregister_subdev(&csi2->subdev);
}

@@ -331,6 +330,10 @@ void atomisp_csi2_configure(struct atomisp_sub_device *asd)
*/
void atomisp_mipi_csi2_cleanup(struct atomisp_device *isp)
{
+ unsigned int i;
+
+ for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++)
+ media_entity_cleanup(&isp->csi2_port[i].subdev.entity);
}

int atomisp_mipi_csi2_init(struct atomisp_device *isp)
diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c
index 3d56ca83ecb7..11d7e04d3ec5 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c
@@ -886,11 +886,16 @@ void atomisp_subdev_cleanup_pending_events(struct atomisp_sub_device *asd)

void atomisp_subdev_unregister_entities(struct atomisp_sub_device *asd)
{
- atomisp_subdev_cleanup_entities(asd);
v4l2_device_unregister_subdev(&asd->subdev);
atomisp_video_unregister(&asd->video_out);
}

+void atomisp_subdev_cleanup(struct atomisp_device *isp)
+{
+ atomisp_subdev_cleanup_entities(&isp->asd);
+ media_entity_cleanup(&isp->asd.video_out.vdev.entity);
+}
+
int atomisp_subdev_register_subdev(struct atomisp_sub_device *asd,
struct v4l2_device *vdev)
{
@@ -913,7 +918,7 @@ int atomisp_subdev_init(struct atomisp_device *isp)
isp_subdev_init_params(&isp->asd);
ret = isp_subdev_init_entities(&isp->asd);
if (ret < 0)
- atomisp_subdev_cleanup_entities(&isp->asd);
+ atomisp_subdev_cleanup(isp);

return ret;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
index 900a67552d6a..5ba9584b81d7 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
@@ -334,10 +334,8 @@ int atomisp_video_init(struct atomisp_video_pipe *video)

void atomisp_video_unregister(struct atomisp_video_pipe *video)
{
- if (video_is_registered(&video->vdev)) {
- media_entity_cleanup(&video->vdev.entity);
+ if (video_is_registered(&video->vdev))
video_unregister_device(&video->vdev);
- }
}

static int atomisp_save_iunit_reg(struct atomisp_device *isp)
@@ -814,7 +812,6 @@ static void atomisp_unregister_entities(struct atomisp_device *isp)

v4l2_device_unregister(&isp->v4l2_dev);
media_device_unregister(&isp->media_dev);
- media_device_cleanup(&isp->media_dev);

for (i = 0; i < isp->input_cnt; i++)
__v4l2_subdev_state_free(isp->inputs[i].try_sd_state);
@@ -875,7 +872,6 @@ static int atomisp_register_entities(struct atomisp_device *isp)
v4l2_device_unregister(&isp->v4l2_dev);
v4l2_device_failed:
media_device_unregister(&isp->media_dev);
- media_device_cleanup(&isp->media_dev);
return ret;
}

@@ -1086,7 +1082,7 @@ static int atomisp_initialize_modules(struct atomisp_device *isp)
ret = atomisp_mipi_csi2_init(isp);
if (ret < 0) {
dev_err(isp->dev, "mipi csi2 initialization failed\n");
- goto error_mipi_csi2;
+ return ret;
}

ret = atomisp_subdev_init(isp);
@@ -1098,13 +1094,13 @@ static int atomisp_initialize_modules(struct atomisp_device *isp)
return 0;

error_isp_subdev:
-error_mipi_csi2:
atomisp_mipi_csi2_cleanup(isp);
return ret;
}

static void atomisp_uninitialize_modules(struct atomisp_device *isp)
{
+ atomisp_subdev_cleanup(isp);
atomisp_mipi_csi2_cleanup(isp);
}

@@ -1451,6 +1447,7 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
atomisp_unregister_entities(isp);
error_uninitialize_modules:
atomisp_uninitialize_modules(isp);
+ media_device_cleanup(&isp->media_dev);
error_irq_uninit:
atomisp_msi_irq_uninit(isp);
pci_free_irq_vectors(pdev);
@@ -1476,6 +1473,7 @@ static void atomisp_pci_remove(struct pci_dev *pdev)

atomisp_unregister_entities(isp);
atomisp_uninitialize_modules(isp);
+ media_device_cleanup(&isp->media_dev);
atomisp_msi_irq_uninit(isp);
pci_free_irq_vectors(pdev);
}
--
2.34.1