linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 00/29] Media device lifetime management
@ 2023-12-20 10:36 Sakari Ailus
  2023-12-20 10:36 ` [PATCH v2 01/29] Revert "[media] media: fix media devnode ioctl/syscall and unregister race" Sakari Ailus
                   ` (30 more replies)
  0 siblings, 31 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

Hi folks,

This is a refresh of my 2016 RFC patchset to start addressing object
lifetime issues in Media controller. It further allows continuing work to
address lifetime management of media entities.

The underlying problem is described in detail in v4 of the previous RFC:
<URL:https://lore.kernel.org/linux-media/20161108135438.GO3217@valkosipuli.retiisi.org.uk/>.
In brief, there is currently no connection between releasing media device
(and related) memory and IOCTL calls, meaning that there is a time window
during which released kernel memory can be accessed, and that access can be
triggered from the user space. The only reason why this is not a grave
security issue is that it is not triggerable by the user alone but requires
unbinding a device. That is still not an excuse for not fixing it.

This set differs from the earlier RFC to address the issue in the
following respects:

- Make changes for ipu3-cio2 driver, too.

- Continue to provide best effort attempt to keep the window between device
  removal and user space being able to access released memory as small as
  possible. This means the problem won't become worse for drivers for which
  Media device lifetime management has not been implemented.

The latter is achieved by adding a new object, Media devnode compat
reference, which is allocated, refcounted and eventually released by the
Media controller framework itself, and where the information on registration
and open filehandles is maintained. This is only done if the driver does not
manage the lifetime of the media device itself, i.e. its release operation
is NULL.

Due to this, Media device file handles will also be introduced by this
patchset. I thought the first user of this would be Media device events but
it seems we already need them here.

Both ipu3-cio2 and omap3isp drivers are relieved of devm_request_irq() use,
as device_release() releases the resources before calling the driver's
remove function. While further work will be required also on these drivers
to safely stop he hardware at unbind time, I don't see a reason not to merge
these patches now.

Some patches are temporarily reverted in order to make reworks easier, then
applied later on.

I've tested this on ipu3-cio2 with and without the refcounting patch (media:
ipu3-cio2: Release the cio2 device context by media device callback),
including failures in a few parts of the driver initialisation process in
the MC framework.

Questions and comments are welcome.

since v1:

- Align subject prefixes with current media tree practices.

- Make release changes to the vimc driver (last patch of the set). This
  was actually easy as vimc already centralised resource release to struct
  v4l2_device, so it was just moved to the media device.

- Move cdev field to struct media_devnode_compat_ref and add dev field to
  the struct, these are needed during device release. This now includes
  also the character device which is accessed by __fput(). I've now tested
  ipu3-cio2 and vimc with KASAN. As a by-product the kref in struct
  media_devnode_compat_ref becomes redundant and is removed. Both devices
  are registered in case of best effort memory safety support and used for
  refcounting.

- Drop omap3isp driver patch moving away from devm_request_irq().

- Add a patch to warn of drivers not releasing media device safely (i.e.
  relying on the best effort memory safety mechanism without refcounting).

- Add a patch to document how the best effort memory release safety helper
  works.

- Add a note on releasing driver's context with the media device, not the
  V4L2 device, in MC documentation.

- Check media device is registered before accessing its fops in
  media_read(), media_write(), media_ioctl and media_compat_ioctl().

- Document best effort media device lifetime management (new patch).

- Use media_devnode_free_minor() in unallocating device node minor number
  in media_devnode_register().

- Continue to rely on devm_register_irq() in ipu3-cio2 driver but register
  the IRQ later on (compared to v1).

- Drop the patch to move away from devm_request_irq() in omap3isp.

- Fix putting references to media device and V4L2 device in 
  v4l2_device_release().

- Add missing media_device_get() (in v1) for M2M devices in
  video_register_media_controller().

- Unconditionally set the media devnode release function in
  media_device_init(). There's no harm doing so and the caller of
  media_device_init() may set the ops after calling the function.

Daniel Axtens (1):
  media: uvcvideo: Refactor teardown of uvc on USB disconnect

Laurent Pinchart (1):
  media: mc: Add per-file-handle data support

Logan Gunthorpe (1):
  media: mc: utilize new cdev_device_add helper function

Sakari Ailus (26):
  Revert "[media] media: fix media devnode ioctl/syscall and unregister
    race"
  Revert "media: utilize new cdev_device_add helper function"
  Revert "[media] media: fix use-after-free in cdev_put() when app exits
    after driver unbind"
  Revert "media: uvcvideo: Refactor teardown of uvc on USB disconnect"
  Revert "[media] media-device: dynamically allocate struct
    media_devnode"
  media: mc: Drop nop release callback
  media: mc: Do not call cdev_device_del() if cdev_device_add() fails
  media: mc: Delete character device early
  media: mc: Split initialising and adding media devnode
  media: mc: Shuffle functions around
  media: mc: Initialise media devnode in media_device_init()
  media: mc: Refactor media devnode minor clearing
  media: mc: Unassign minor only if it has been assigned
  media: mc: Refcount the media device
  media: v4l: Acquire a reference to the media device for every video
    device
  media: mc: Postpone graph object removal until free
  media: omap3isp: Release the isp device struct by media device
    callback
  media: ipu3-cio2: Call v4l2_device_unregister() earlier
  media: ipu3-cio2: Request IRQ earlier
  media: ipu3-cio2: Release the cio2 device context by media device
    callback
  media: vimc: Release resources on media device release
  media: Documentation: Document how Media device resources are released
  media: mc: Maintain a list of open file handles in a media device
  media: mc: Implement best effort media device removal safety sans
    refcount
  media: mc: Warn about drivers not releasing media device safely
  media: Documentation: Document media device memory safety helper

 Documentation/driver-api/media/mc-core.rst  |  18 +-
 drivers/media/cec/core/cec-core.c           |   2 +-
 drivers/media/mc/mc-device.c                | 260 ++++++++++++--------
 drivers/media/mc/mc-devnode.c               | 230 +++++++++++------
 drivers/media/pci/intel/ipu3/ipu3-cio2.c    |  70 ++++--
 drivers/media/platform/ti/omap3isp/isp.c    |  24 +-
 drivers/media/test-drivers/vimc/vimc-core.c |  15 +-
 drivers/media/usb/au0828/au0828-core.c      |   4 +-
 drivers/media/usb/uvc/uvc_driver.c          |   2 +-
 drivers/media/v4l2-core/v4l2-dev.c          |  65 +++--
 drivers/staging/media/sunxi/cedrus/cedrus.c |   2 +-
 include/media/media-device.h                |  46 +++-
 include/media/media-devnode.h               | 136 +++++++---
 include/media/media-fh.h                    |  32 +++
 14 files changed, 632 insertions(+), 274 deletions(-)
 create mode 100644 include/media/media-fh.h

-- 
2.39.2


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v2 01/29] Revert "[media] media: fix media devnode ioctl/syscall and unregister race"
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2023-12-20 10:36 ` [PATCH v2 02/29] Revert "media: utilize new cdev_device_add helper function" Sakari Ailus
                   ` (29 subsequent siblings)
  30 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

This reverts commit 6f0dd24a084a ("[media] media: fix media devnode
ioctl/syscall and unregister race"). The commit was part of an original
patchset to avoid crashes when an unregistering device is in use.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/mc/mc-device.c  | 15 +++++++--------
 drivers/media/mc/mc-devnode.c |  8 +-------
 include/media/media-devnode.h | 16 ++--------------
 3 files changed, 10 insertions(+), 29 deletions(-)

diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index c0dd4ae57227..6c569ecd4b3d 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -741,7 +741,6 @@ int __must_check __media_device_register(struct media_device *mdev,
 	if (ret < 0) {
 		/* devnode free is handled in media_devnode_*() */
 		mdev->devnode = NULL;
-		media_devnode_unregister_prepare(devnode);
 		media_devnode_unregister(devnode);
 		return ret;
 	}
@@ -797,9 +796,6 @@ void media_device_unregister(struct media_device *mdev)
 		return;
 	}
 
-	/* Clear the devnode register bit to avoid races with media dev open */
-	media_devnode_unregister_prepare(mdev->devnode);
-
 	/* Remove all entities from the media device */
 	list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
 		__media_device_unregister_entity(entity);
@@ -824,10 +820,13 @@ void media_device_unregister(struct media_device *mdev)
 
 	dev_dbg(mdev->dev, "Media device unregistered\n");
 
-	device_remove_file(&mdev->devnode->dev, &dev_attr_model);
-	media_devnode_unregister(mdev->devnode);
-	/* devnode free is handled in media_devnode_*() */
-	mdev->devnode = NULL;
+	/* Check if mdev devnode was registered */
+	if (media_devnode_is_registered(mdev->devnode)) {
+		device_remove_file(&mdev->devnode->dev, &dev_attr_model);
+		media_devnode_unregister(mdev->devnode);
+		/* devnode free is handled in media_devnode_*() */
+		mdev->devnode = NULL;
+	}
 }
 EXPORT_SYMBOL_GPL(media_device_unregister);
 
diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 680fbb3a9340..0ab33214d243 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -267,7 +267,7 @@ int __must_check media_devnode_register(struct media_device *mdev,
 	return ret;
 }
 
-void media_devnode_unregister_prepare(struct media_devnode *devnode)
+void media_devnode_unregister(struct media_devnode *devnode)
 {
 	/* Check if devnode was ever registered at all */
 	if (!media_devnode_is_registered(devnode))
@@ -275,12 +275,6 @@ void media_devnode_unregister_prepare(struct media_devnode *devnode)
 
 	mutex_lock(&media_devnode_lock);
 	clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
-	mutex_unlock(&media_devnode_lock);
-}
-
-void media_devnode_unregister(struct media_devnode *devnode)
-{
-	mutex_lock(&media_devnode_lock);
 	/* Delete the cdev on this minor as well */
 	cdev_device_del(&devnode->cdev, &devnode->dev);
 	devnode->media_dev = NULL;
diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
index d27c1c646c28..46f0d3ae44d1 100644
--- a/include/media/media-devnode.h
+++ b/include/media/media-devnode.h
@@ -115,19 +115,6 @@ int __must_check media_devnode_register(struct media_device *mdev,
 					struct media_devnode *devnode,
 					struct module *owner);
 
-/**
- * media_devnode_unregister_prepare - clear the media device node register bit
- * @devnode: the device node to prepare for unregister
- *
- * This clears the passed device register bit. Future open calls will be met
- * with errors. Should be called before media_devnode_unregister() to avoid
- * races with unregister and device file open calls.
- *
- * This function can safely be called if the device node has never been
- * registered or has already been unregistered.
- */
-void media_devnode_unregister_prepare(struct media_devnode *devnode);
-
 /**
  * media_devnode_unregister - unregister a media device node
  * @devnode: the device node to unregister
@@ -135,7 +122,8 @@ void media_devnode_unregister_prepare(struct media_devnode *devnode);
  * This unregisters the passed device. Future open calls will be met with
  * errors.
  *
- * Should be called after media_devnode_unregister_prepare()
+ * This function can safely be called if the device node has never been
+ * registered or has already been unregistered.
  */
 void media_devnode_unregister(struct media_devnode *devnode);
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 02/29] Revert "media: utilize new cdev_device_add helper function"
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
  2023-12-20 10:36 ` [PATCH v2 01/29] Revert "[media] media: fix media devnode ioctl/syscall and unregister race" Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2023-12-20 10:36 ` [PATCH v2 03/29] Revert "[media] media: fix use-after-free in cdev_put() when app exits after driver unbind" Sakari Ailus
                   ` (28 subsequent siblings)
  30 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

This reverts commit 857313e51006ff51524579bcd8808b70f9a80812. This patch is
temporarily reverted for internal rework.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/cec/core/cec-core.c | 16 ++++++++++++----
 drivers/media/mc/mc-devnode.c     | 21 ++++++++++++++++-----
 2 files changed, 28 insertions(+), 9 deletions(-)

diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
index 7e153c5cad04..0645e68411fb 100644
--- a/drivers/media/cec/core/cec-core.c
+++ b/drivers/media/cec/core/cec-core.c
@@ -137,19 +137,26 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode,
 
 	/* Part 2: Initialize and register the character device */
 	cdev_init(&devnode->cdev, &cec_devnode_fops);
+	devnode->cdev.kobj.parent = &devnode->dev.kobj;
 	devnode->cdev.owner = owner;
 	kobject_set_name(&devnode->cdev.kobj, "cec%d", devnode->minor);
 
 	devnode->registered = true;
-	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
-	if (ret) {
+	ret = cdev_add(&devnode->cdev, devnode->dev.devt, 1);
+	if (ret < 0) {
+		pr_err("%s: cdev_add failed\n", __func__);
 		devnode->registered = false;
-		pr_err("%s: cdev_device_add failed\n", __func__);
 		goto clr_bit;
 	}
 
+	ret = device_add(&devnode->dev);
+	if (ret)
+		goto cdev_del;
+
 	return 0;
 
+cdev_del:
+	cdev_del(&devnode->cdev);
 clr_bit:
 	mutex_lock(&cec_devnode_lock);
 	clear_bit(devnode->minor, cec_devnode_nums);
@@ -195,7 +202,8 @@ static void cec_devnode_unregister(struct cec_adapter *adap)
 	cec_adap_enable(adap);
 	mutex_unlock(&adap->lock);
 
-	cdev_device_del(&devnode->cdev, &devnode->dev);
+	device_del(&devnode->dev);
+	cdev_del(&devnode->cdev);
 	put_device(&devnode->dev);
 }
 
diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 0ab33214d243..740573552e5d 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -240,23 +240,33 @@ int __must_check media_devnode_register(struct media_device *mdev,
 	dev_set_name(&devnode->dev, "media%d", devnode->minor);
 	device_initialize(&devnode->dev);
 
-	/* Part 2: Initialize the character device */
+	/* Part 2: Initialize and register the character device */
 	cdev_init(&devnode->cdev, &media_devnode_fops);
 	devnode->cdev.owner = owner;
+	devnode->cdev.kobj.parent = &devnode->dev.kobj;
 	kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor);
 
-	/* Part 3: Add the media and char device */
-	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
+	ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t),
+					     devnode->minor), 1);
 	if (ret < 0) {
-		pr_err("%s: cdev_device_add failed\n", __func__);
+		pr_err("%s: cdev_add failed\n", __func__);
 		goto cdev_add_error;
 	}
 
+	/* Part 3: Add the media device */
+	ret = device_add(&devnode->dev);
+	if (ret < 0) {
+		pr_err("%s: device_add failed\n", __func__);
+		goto device_add_error;
+	}
+
 	/* Part 4: Activate this minor. The char device can now be used. */
 	set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
 
 	return 0;
 
+device_add_error:
+	cdev_del(&devnode->cdev);
 cdev_add_error:
 	mutex_lock(&media_devnode_lock);
 	clear_bit(devnode->minor, media_devnode_nums);
@@ -276,9 +286,10 @@ void media_devnode_unregister(struct media_devnode *devnode)
 	mutex_lock(&media_devnode_lock);
 	clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
 	/* Delete the cdev on this minor as well */
-	cdev_device_del(&devnode->cdev, &devnode->dev);
+	cdev_del(&devnode->cdev);
 	devnode->media_dev = NULL;
 	mutex_unlock(&media_devnode_lock);
+	device_del(&devnode->dev);
 
 	put_device(&devnode->dev);
 }
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 03/29] Revert "[media] media: fix use-after-free in cdev_put() when app exits after driver unbind"
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
  2023-12-20 10:36 ` [PATCH v2 01/29] Revert "[media] media: fix media devnode ioctl/syscall and unregister race" Sakari Ailus
  2023-12-20 10:36 ` [PATCH v2 02/29] Revert "media: utilize new cdev_device_add helper function" Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2023-12-20 10:36 ` [PATCH v2 04/29] media: mc: utilize new cdev_device_add helper function Sakari Ailus
                   ` (27 subsequent siblings)
  30 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

This reverts commit 5b28dde51d0c ("[media] media: fix use-after-free in
cdev_put() when app exits after driver unbind"). The commit was part of an
original patchset to avoid crashes when an unregistering device is in use.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/mc/mc-device.c  |  6 ++---
 drivers/media/mc/mc-devnode.c | 48 ++++++++++++++---------------------
 2 files changed, 21 insertions(+), 33 deletions(-)

diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index 6c569ecd4b3d..4772a7f55112 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -732,16 +732,16 @@ int __must_check __media_device_register(struct media_device *mdev,
 
 	ret = media_devnode_register(mdev, devnode, owner);
 	if (ret < 0) {
-		/* devnode free is handled in media_devnode_*() */
 		mdev->devnode = NULL;
+		kfree(devnode);
 		return ret;
 	}
 
 	ret = device_create_file(&devnode->dev, &dev_attr_model);
 	if (ret < 0) {
-		/* devnode free is handled in media_devnode_*() */
 		mdev->devnode = NULL;
 		media_devnode_unregister(devnode);
+		kfree(devnode);
 		return ret;
 	}
 
@@ -824,8 +824,6 @@ void media_device_unregister(struct media_device *mdev)
 	if (media_devnode_is_registered(mdev->devnode)) {
 		device_remove_file(&mdev->devnode->dev, &dev_attr_model);
 		media_devnode_unregister(mdev->devnode);
-		/* devnode free is handled in media_devnode_*() */
-		mdev->devnode = NULL;
 	}
 }
 EXPORT_SYMBOL_GPL(media_device_unregister);
diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 740573552e5d..1e1792c3ae3f 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -51,8 +51,13 @@ static void media_devnode_release(struct device *cd)
 	struct media_devnode *devnode = to_media_devnode(cd);
 
 	mutex_lock(&media_devnode_lock);
+
+	/* Delete the cdev on this minor as well */
+	cdev_del(&devnode->cdev);
+
 	/* Mark device node number as free */
 	clear_bit(devnode->minor, media_devnode_nums);
+
 	mutex_unlock(&media_devnode_lock);
 
 	/* Release media_devnode and perform other cleanups as needed. */
@@ -60,7 +65,6 @@ static void media_devnode_release(struct device *cd)
 		devnode->release(devnode);
 
 	kfree(devnode);
-	pr_debug("%s: Media Devnode Deallocated\n", __func__);
 }
 
 static struct bus_type media_bus_type = {
@@ -189,8 +193,6 @@ static int media_release(struct inode *inode, struct file *filp)
 	/* decrease the refcount unconditionally since the release()
 	   return value is ignored. */
 	put_device(&devnode->dev);
-
-	pr_debug("%s: Media Release\n", __func__);
 	return 0;
 }
 
@@ -221,7 +223,6 @@ int __must_check media_devnode_register(struct media_device *mdev,
 	if (minor == MEDIA_NUM_DEVICES) {
 		mutex_unlock(&media_devnode_lock);
 		pr_err("could not get a free minor\n");
-		kfree(devnode);
 		return -ENFILE;
 	}
 
@@ -231,33 +232,29 @@ int __must_check media_devnode_register(struct media_device *mdev,
 	devnode->minor = minor;
 	devnode->media_dev = mdev;
 
-	/* Part 1: Initialize dev now to use dev.kobj for cdev.kobj.parent */
-	devnode->dev.bus = &media_bus_type;
-	devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
-	devnode->dev.release = media_devnode_release;
-	if (devnode->parent)
-		devnode->dev.parent = devnode->parent;
-	dev_set_name(&devnode->dev, "media%d", devnode->minor);
-	device_initialize(&devnode->dev);
-
 	/* Part 2: Initialize and register the character device */
 	cdev_init(&devnode->cdev, &media_devnode_fops);
 	devnode->cdev.owner = owner;
-	devnode->cdev.kobj.parent = &devnode->dev.kobj;
 	kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor);
 
 	ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t),
 					     devnode->minor), 1);
 	if (ret < 0) {
 		pr_err("%s: cdev_add failed\n", __func__);
-		goto cdev_add_error;
+		goto error;
 	}
 
-	/* Part 3: Add the media device */
-	ret = device_add(&devnode->dev);
+	/* Part 3: Register the media device */
+	devnode->dev.bus = &media_bus_type;
+	devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
+	devnode->dev.release = media_devnode_release;
+	if (devnode->parent)
+		devnode->dev.parent = devnode->parent;
+	dev_set_name(&devnode->dev, "media%d", devnode->minor);
+	ret = device_register(&devnode->dev);
 	if (ret < 0) {
-		pr_err("%s: device_add failed\n", __func__);
-		goto device_add_error;
+		pr_err("%s: device_register failed\n", __func__);
+		goto error;
 	}
 
 	/* Part 4: Activate this minor. The char device can now be used. */
@@ -265,15 +262,12 @@ int __must_check media_devnode_register(struct media_device *mdev,
 
 	return 0;
 
-device_add_error:
-	cdev_del(&devnode->cdev);
-cdev_add_error:
+error:
 	mutex_lock(&media_devnode_lock);
+	cdev_del(&devnode->cdev);
 	clear_bit(devnode->minor, media_devnode_nums);
-	devnode->media_dev = NULL;
 	mutex_unlock(&media_devnode_lock);
 
-	put_device(&devnode->dev);
 	return ret;
 }
 
@@ -285,13 +279,9 @@ void media_devnode_unregister(struct media_devnode *devnode)
 
 	mutex_lock(&media_devnode_lock);
 	clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
-	/* Delete the cdev on this minor as well */
-	cdev_del(&devnode->cdev);
-	devnode->media_dev = NULL;
 	mutex_unlock(&media_devnode_lock);
-	device_del(&devnode->dev);
 
-	put_device(&devnode->dev);
+	device_unregister(&devnode->dev);
 }
 
 /*
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 04/29] media: mc: utilize new cdev_device_add helper function
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (2 preceding siblings ...)
  2023-12-20 10:36 ` [PATCH v2 03/29] Revert "[media] media: fix use-after-free in cdev_put() when app exits after driver unbind" Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2024-02-07  9:38   ` Laurent Pinchart
  2023-12-20 10:36 ` [PATCH v2 05/29] Revert "media: uvcvideo: Refactor teardown of uvc on USB disconnect" Sakari Ailus
                   ` (26 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

From: Logan Gunthorpe <logang@deltatee.com>

Replace the open coded registration of the cdev and dev with the
new device_add_cdev() helper. The helper replaces a common pattern by
taking the proper reference against the parent device and adding both
the cdev and the device.

Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 drivers/media/cec/core/cec-core.c | 16 ++++------------
 drivers/media/mc/mc-devnode.c     | 23 +++++++++--------------
 2 files changed, 13 insertions(+), 26 deletions(-)

diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
index 0645e68411fb..15494b46458a 100644
--- a/drivers/media/cec/core/cec-core.c
+++ b/drivers/media/cec/core/cec-core.c
@@ -137,26 +137,19 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode,
 
 	/* Part 2: Initialize and register the character device */
 	cdev_init(&devnode->cdev, &cec_devnode_fops);
-	devnode->cdev.kobj.parent = &devnode->dev.kobj;
 	devnode->cdev.owner = owner;
 	kobject_set_name(&devnode->cdev.kobj, "cec%d", devnode->minor);
 
 	devnode->registered = true;
-	ret = cdev_add(&devnode->cdev, devnode->dev.devt, 1);
-	if (ret < 0) {
-		pr_err("%s: cdev_add failed\n", __func__);
+	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
+	if (ret) {
+		pr_err("%s: cdev_device_add failed\n", __func__);
 		devnode->registered = false;
 		goto clr_bit;
 	}
 
-	ret = device_add(&devnode->dev);
-	if (ret)
-		goto cdev_del;
-
 	return 0;
 
-cdev_del:
-	cdev_del(&devnode->cdev);
 clr_bit:
 	mutex_lock(&cec_devnode_lock);
 	clear_bit(devnode->minor, cec_devnode_nums);
@@ -202,8 +195,7 @@ static void cec_devnode_unregister(struct cec_adapter *adap)
 	cec_adap_enable(adap);
 	mutex_unlock(&adap->lock);
 
-	device_del(&devnode->dev);
-	cdev_del(&devnode->cdev);
+	cdev_device_del(&devnode->cdev, &devnode->dev);
 	put_device(&devnode->dev);
 }
 
diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 1e1792c3ae3f..fabcd646679b 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -232,29 +232,24 @@ int __must_check media_devnode_register(struct media_device *mdev,
 	devnode->minor = minor;
 	devnode->media_dev = mdev;
 
-	/* Part 2: Initialize and register the character device */
+	/* Part 2: Initialize the media and character devices */
 	cdev_init(&devnode->cdev, &media_devnode_fops);
 	devnode->cdev.owner = owner;
 	kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor);
 
-	ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t),
-					     devnode->minor), 1);
-	if (ret < 0) {
-		pr_err("%s: cdev_add failed\n", __func__);
-		goto error;
-	}
-
-	/* Part 3: Register the media device */
 	devnode->dev.bus = &media_bus_type;
 	devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
 	devnode->dev.release = media_devnode_release;
 	if (devnode->parent)
 		devnode->dev.parent = devnode->parent;
 	dev_set_name(&devnode->dev, "media%d", devnode->minor);
-	ret = device_register(&devnode->dev);
+	device_initialize(&devnode->dev);
+
+	/* Part 3: Add the media and character devices */
+	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
 	if (ret < 0) {
-		pr_err("%s: device_register failed\n", __func__);
-		goto error;
+		pr_err("%s: cdev_device_add failed\n", __func__);
+		goto cdev_add_error;
 	}
 
 	/* Part 4: Activate this minor. The char device can now be used. */
@@ -262,9 +257,9 @@ int __must_check media_devnode_register(struct media_device *mdev,
 
 	return 0;
 
-error:
+cdev_add_error:
 	mutex_lock(&media_devnode_lock);
-	cdev_del(&devnode->cdev);
+	cdev_device_del(&devnode->cdev, &devnode->dev);
 	clear_bit(devnode->minor, media_devnode_nums);
 	mutex_unlock(&media_devnode_lock);
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 05/29] Revert "media: uvcvideo: Refactor teardown of uvc on USB disconnect"
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (3 preceding siblings ...)
  2023-12-20 10:36 ` [PATCH v2 04/29] media: mc: utilize new cdev_device_add helper function Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2023-12-20 10:36 ` [PATCH v2 06/29] Revert "[media] media-device: dynamically allocate struct media_devnode" Sakari Ailus
                   ` (25 subsequent siblings)
  30 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

This reverts commit 10e1fdb95809ed21406f53b5b4f064673a1b9ceb.

Temporarily revert this patch to revert a dependent patch. The patch is
re-applied later, rebased on the revert.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/usb/uvc/uvc_driver.c | 13 ++++---------
 drivers/media/usb/uvc/uvc_status.c | 12 ++++--------
 drivers/media/usb/uvc/uvcvideo.h   |  1 -
 3 files changed, 8 insertions(+), 18 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index bbd90123a4e7..fe8b251f47e7 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1859,7 +1859,11 @@ static void uvc_delete(struct kref *kref)
 	usb_put_intf(dev->intf);
 	usb_put_dev(dev->udev);
 
+	if (dev->vdev.dev)
+		v4l2_device_unregister(&dev->vdev);
 #ifdef CONFIG_MEDIA_CONTROLLER
+	if (media_devnode_is_registered(dev->mdev.devnode))
+		media_device_unregister(&dev->mdev);
 	media_device_cleanup(&dev->mdev);
 #endif
 
@@ -1916,15 +1920,6 @@ static void uvc_unregister_video(struct uvc_device *dev)
 
 		uvc_debugfs_cleanup_stream(stream);
 	}
-
-	uvc_status_unregister(dev);
-
-	if (dev->vdev.dev)
-		v4l2_device_unregister(&dev->vdev);
-#ifdef CONFIG_MEDIA_CONTROLLER
-	if (media_devnode_is_registered(dev->mdev.devnode))
-		media_device_unregister(&dev->mdev);
-#endif
 }
 
 int uvc_register_video_device(struct uvc_device *dev,
diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c
index a78a88c710e2..015be0886801 100644
--- a/drivers/media/usb/uvc/uvc_status.c
+++ b/drivers/media/usb/uvc/uvc_status.c
@@ -73,7 +73,7 @@ static int uvc_input_init(struct uvc_device *dev)
 	return ret;
 }
 
-static void uvc_input_unregister(struct uvc_device *dev)
+static void uvc_input_cleanup(struct uvc_device *dev)
 {
 	if (dev->input)
 		input_unregister_device(dev->input);
@@ -90,7 +90,7 @@ static void uvc_input_report_key(struct uvc_device *dev, unsigned int code,
 
 #else
 #define uvc_input_init(dev)
-#define uvc_input_unregister(dev)
+#define uvc_input_cleanup(dev)
 #define uvc_input_report_key(dev, code, value)
 #endif /* CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV */
 
@@ -290,16 +290,12 @@ int uvc_status_init(struct uvc_device *dev)
 	return 0;
 }
 
-void uvc_status_unregister(struct uvc_device *dev)
-{
-	usb_kill_urb(dev->int_urb);
-	uvc_input_unregister(dev);
-}
-
 void uvc_status_cleanup(struct uvc_device *dev)
 {
+	usb_kill_urb(dev->int_urb);
 	usb_free_urb(dev->int_urb);
 	kfree(dev->status);
+	uvc_input_cleanup(dev);
 }
 
 int uvc_status_start(struct uvc_device *dev, gfp_t flags)
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 6fb0a78b1b00..ab8de60f5de2 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -742,7 +742,6 @@ int uvc_register_video_device(struct uvc_device *dev,
 
 /* Status */
 int uvc_status_init(struct uvc_device *dev);
-void uvc_status_unregister(struct uvc_device *dev);
 void uvc_status_cleanup(struct uvc_device *dev);
 int uvc_status_start(struct uvc_device *dev, gfp_t flags);
 void uvc_status_stop(struct uvc_device *dev);
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 06/29] Revert "[media] media-device: dynamically allocate struct media_devnode"
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (4 preceding siblings ...)
  2023-12-20 10:36 ` [PATCH v2 05/29] Revert "media: uvcvideo: Refactor teardown of uvc on USB disconnect" Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2023-12-20 10:36 ` [PATCH v2 07/29] media: uvcvideo: Refactor teardown of uvc on USB disconnect Sakari Ailus
                   ` (24 subsequent siblings)
  30 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

This reverts commit a087ce704b80 ("[media] media-device: dynamically
allocate struct media_devnode"). The commit was part of an original
patchset to avoid crashes when an unregistering device is in use.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/mc/mc-device.c                | 44 +++++++--------------
 drivers/media/mc/mc-devnode.c               |  7 +---
 drivers/media/usb/au0828/au0828-core.c      |  4 +-
 drivers/media/usb/uvc/uvc_driver.c          |  2 +-
 drivers/staging/media/sunxi/cedrus/cedrus.c |  2 +-
 include/media/media-device.h                |  5 ++-
 include/media/media-devnode.h               | 15 +------
 7 files changed, 25 insertions(+), 54 deletions(-)

diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index 4772a7f55112..d4553a3705f5 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -435,7 +435,7 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd,
 			       unsigned long __arg)
 {
 	struct media_devnode *devnode = media_devnode_data(filp);
-	struct media_device *dev = devnode->media_dev;
+	struct media_device *dev = to_media_device(devnode);
 	const struct media_ioctl_info *info;
 	void __user *arg = (void __user *)__arg;
 	char __karg[256], *karg = __karg;
@@ -519,7 +519,7 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd,
 				      unsigned long arg)
 {
 	struct media_devnode *devnode = media_devnode_data(filp);
-	struct media_device *dev = devnode->media_dev;
+	struct media_device *dev = to_media_device(devnode);
 	long ret;
 
 	switch (cmd) {
@@ -555,8 +555,7 @@ static const struct media_file_operations media_device_fops = {
 static ssize_t model_show(struct device *cd,
 			  struct device_attribute *attr, char *buf)
 {
-	struct media_devnode *devnode = to_media_devnode(cd);
-	struct media_device *mdev = devnode->media_dev;
+	struct media_device *mdev = to_media_device(to_media_devnode(cd));
 
 	return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model);
 }
@@ -714,34 +713,23 @@ EXPORT_SYMBOL_GPL(media_device_cleanup);
 int __must_check __media_device_register(struct media_device *mdev,
 					 struct module *owner)
 {
-	struct media_devnode *devnode;
 	int ret;
 
-	devnode = kzalloc(sizeof(*devnode), GFP_KERNEL);
-	if (!devnode)
-		return -ENOMEM;
-
 	/* Register the device node. */
-	mdev->devnode = devnode;
-	devnode->fops = &media_device_fops;
-	devnode->parent = mdev->dev;
-	devnode->release = media_device_release;
+	mdev->devnode.fops = &media_device_fops;
+	mdev->devnode.parent = mdev->dev;
+	mdev->devnode.release = media_device_release;
 
 	/* Set version 0 to indicate user-space that the graph is static */
 	mdev->topology_version = 0;
 
-	ret = media_devnode_register(mdev, devnode, owner);
-	if (ret < 0) {
-		mdev->devnode = NULL;
-		kfree(devnode);
+	ret = media_devnode_register(&mdev->devnode, owner);
+	if (ret < 0)
 		return ret;
-	}
 
-	ret = device_create_file(&devnode->dev, &dev_attr_model);
+	ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
 	if (ret < 0) {
-		mdev->devnode = NULL;
-		media_devnode_unregister(devnode);
-		kfree(devnode);
+		media_devnode_unregister(&mdev->devnode);
 		return ret;
 	}
 
@@ -791,7 +779,7 @@ void media_device_unregister(struct media_device *mdev)
 	mutex_lock(&mdev->graph_mutex);
 
 	/* Check if mdev was ever registered at all */
-	if (!media_devnode_is_registered(mdev->devnode)) {
+	if (!media_devnode_is_registered(&mdev->devnode)) {
 		mutex_unlock(&mdev->graph_mutex);
 		return;
 	}
@@ -818,13 +806,9 @@ void media_device_unregister(struct media_device *mdev)
 
 	mutex_unlock(&mdev->graph_mutex);
 
-	dev_dbg(mdev->dev, "Media device unregistered\n");
-
-	/* Check if mdev devnode was registered */
-	if (media_devnode_is_registered(mdev->devnode)) {
-		device_remove_file(&mdev->devnode->dev, &dev_attr_model);
-		media_devnode_unregister(mdev->devnode);
-	}
+	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
+	dev_dbg(mdev->dev, "Media device unregistering\n");
+	media_devnode_unregister(&mdev->devnode);
 }
 EXPORT_SYMBOL_GPL(media_device_unregister);
 
diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index fabcd646679b..ce93ab9be676 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -32,7 +32,6 @@
 #include <linux/uaccess.h>
 
 #include <media/media-devnode.h>
-#include <media/media-device.h>
 
 #define MEDIA_NUM_DEVICES	256
 #define MEDIA_NAME		"media"
@@ -63,8 +62,6 @@ static void media_devnode_release(struct device *cd)
 	/* Release media_devnode and perform other cleanups as needed. */
 	if (devnode->release)
 		devnode->release(devnode);
-
-	kfree(devnode);
 }
 
 static struct bus_type media_bus_type = {
@@ -210,8 +207,7 @@ static const struct file_operations media_devnode_fops = {
 	.llseek = no_llseek,
 };
 
-int __must_check media_devnode_register(struct media_device *mdev,
-					struct media_devnode *devnode,
+int __must_check media_devnode_register(struct media_devnode *devnode,
 					struct module *owner)
 {
 	int minor;
@@ -230,7 +226,6 @@ int __must_check media_devnode_register(struct media_device *mdev,
 	mutex_unlock(&media_devnode_lock);
 
 	devnode->minor = minor;
-	devnode->media_dev = mdev;
 
 	/* Part 2: Initialize the media and character devices */
 	cdev_init(&devnode->cdev, &media_devnode_fops);
diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c
index 1e246b47766d..4101e12cda5a 100644
--- a/drivers/media/usb/au0828/au0828-core.c
+++ b/drivers/media/usb/au0828/au0828-core.c
@@ -128,7 +128,7 @@ static void au0828_unregister_media_device(struct au0828_dev *dev)
 	struct media_device *mdev = dev->media_dev;
 	struct media_entity_notify *notify, *nextp;
 
-	if (!mdev || !media_devnode_is_registered(mdev->devnode))
+	if (!mdev || !media_devnode_is_registered(&mdev->devnode))
 		return;
 
 	/* Remove au0828 entity_notify callbacks */
@@ -566,7 +566,7 @@ static int au0828_media_device_register(struct au0828_dev *dev,
 	if (!dev->media_dev)
 		return 0;
 
-	if (!media_devnode_is_registered(dev->media_dev->devnode)) {
+	if (!media_devnode_is_registered(&dev->media_dev->devnode)) {
 
 		/* register media device */
 		ret = media_device_register(dev->media_dev);
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index fe8b251f47e7..1044e13976b5 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1862,7 +1862,7 @@ static void uvc_delete(struct kref *kref)
 	if (dev->vdev.dev)
 		v4l2_device_unregister(&dev->vdev);
 #ifdef CONFIG_MEDIA_CONTROLLER
-	if (media_devnode_is_registered(dev->mdev.devnode))
+	if (media_devnode_is_registered(&dev->mdev.devnode))
 		media_device_unregister(&dev->mdev);
 	media_device_cleanup(&dev->mdev);
 #endif
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c
index f52df6836045..f8f678835a4c 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.c
@@ -548,7 +548,7 @@ static void cedrus_remove(struct platform_device *pdev)
 	struct cedrus_dev *dev = platform_get_drvdata(pdev);
 
 	cancel_delayed_work_sync(&dev->watchdog_work);
-	if (media_devnode_is_registered(dev->mdev.devnode)) {
+	if (media_devnode_is_registered(&dev->mdev.devnode)) {
 		media_device_unregister(&dev->mdev);
 		v4l2_m2m_unregister_media_controller(dev->m2m_dev);
 		media_device_cleanup(&dev->mdev);
diff --git a/include/media/media-device.h b/include/media/media-device.h
index 2c146d0b2b1c..fb0855b217ce 100644
--- a/include/media/media-device.h
+++ b/include/media/media-device.h
@@ -145,7 +145,7 @@ struct media_device_ops {
 struct media_device {
 	/* dev->driver_data points to this struct. */
 	struct device *dev;
-	struct media_devnode *devnode;
+	struct media_devnode devnode;
 
 	char model[32];
 	char driver_name[32];
@@ -191,6 +191,9 @@ struct usb_device;
 #define MEDIA_DEV_NOTIFY_PRE_LINK_CH	0
 #define MEDIA_DEV_NOTIFY_POST_LINK_CH	1
 
+/* media_devnode to media_device */
+#define to_media_device(node) container_of(node, struct media_device, devnode)
+
 /**
  * media_device_init() - Initializes a media device element
  *
diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
index 46f0d3ae44d1..1117d1dfd6bf 100644
--- a/include/media/media-devnode.h
+++ b/include/media/media-devnode.h
@@ -21,8 +21,6 @@
 #include <linux/device.h>
 #include <linux/cdev.h>
 
-struct media_device;
-
 /*
  * Flag to mark the media_devnode struct as registered. Drivers must not touch
  * this flag directly, it will be set and cleared by media_devnode_register and
@@ -73,8 +71,6 @@ struct media_file_operations {
  * before registering the node.
  */
 struct media_devnode {
-	struct media_device *media_dev;
-
 	/* device ops */
 	const struct media_file_operations *fops;
 
@@ -97,8 +93,7 @@ struct media_devnode {
 /**
  * media_devnode_register - register a media device node
  *
- * @mdev: struct media_device we want to register a device node
- * @devnode: media device node structure we want to register
+ * @devnode: struct media_devnode we want to register a device node
  * @owner: should be filled with %THIS_MODULE
  *
  * The registration code assigns minor numbers and registers the new device node
@@ -111,8 +106,7 @@ struct media_devnode {
  * the media_devnode structure is *not* called, so the caller is responsible for
  * freeing any data.
  */
-int __must_check media_devnode_register(struct media_device *mdev,
-					struct media_devnode *devnode,
+int __must_check media_devnode_register(struct media_devnode *devnode,
 					struct module *owner);
 
 /**
@@ -142,14 +136,9 @@ static inline struct media_devnode *media_devnode_data(struct file *filp)
  *	false otherwise.
  *
  * @devnode: pointer to struct &media_devnode.
- *
- * Note: If mdev is NULL, it also returns false.
  */
 static inline int media_devnode_is_registered(struct media_devnode *devnode)
 {
-	if (!devnode)
-		return false;
-
 	return test_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
 }
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 07/29] media: uvcvideo: Refactor teardown of uvc on USB disconnect
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (5 preceding siblings ...)
  2023-12-20 10:36 ` [PATCH v2 06/29] Revert "[media] media-device: dynamically allocate struct media_devnode" Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2023-12-20 10:36 ` [PATCH v2 08/29] media: mc: Drop nop release callback Sakari Ailus
                   ` (23 subsequent siblings)
  30 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

From: Daniel Axtens <dja@axtens.net>

Currently, disconnecting a USB webcam while it is in use prints out a
number of warnings, such as:

WARNING: CPU: 2 PID: 3118 at /build/linux-ezBi1T/linux-4.8.0/fs/sysfs/group.c:237 sysfs_remove_group+0x8b/0x90
sysfs group ffffffffa7cd0780 not found for kobject 'event13'

This has been noticed before. [0]

This is because of the order in which things are torn down.

If there are no streams active during a USB disconnect:

 - uvc_disconnect() is invoked via device_del() through the bus
   notifier mechanism.

 - this calls uvc_unregister_video().

 - uvc_unregister_video() unregisters the video device for each
   stream,

 - because there are no streams open, it calls uvc_delete()

 - uvc_delete() calls uvc_status_cleanup(), which cleans up the status
   input device.

 - uvc_delete() calls media_device_unregister(), which cleans up the
   media device

 - uvc_delete(), uvc_unregister_video() and uvc_disconnect() all
   return, and we end up back in device_del().

 - device_del() then cleans up the sysfs folder for the camera with
   dpm_sysfs_remove(). Because uvc_status_cleanup() and
   media_device_unregister() have already been called, this all works
   nicely.

If, on the other hand, there *are* streams active during a USB disconnect:

 - uvc_disconnect() is invoked

 - this calls uvc_unregister_video()

 - uvc_unregister_video() unregisters the video device for each
   stream,

 - uvc_unregister_video() and uvc_disconnect() return, and we end up
   back in device_del().

 - device_del() then cleans up the sysfs folder for the camera with
   dpm_sysfs_remove(). Because the status input device and the media
   device are children of the USB device, this also deletes their
   sysfs folders.

 - Sometime later, the final stream is closed, invoking uvc_release().

 - uvc_release() calls uvc_delete()

 - uvc_delete() calls uvc_status_cleanup(), which cleans up the status
   input device. Because the sysfs directory has already been removed,
   this causes a WARNing.

 - uvc_delete() calls media_device_unregister(), which cleans up the
   media device. Because the sysfs directory has already been removed,
   this causes another WARNing.

To fix this, we need to make sure the devices are always unregistered
before the end of uvc_disconnect(). To this, move the unregistration
into the disconnect path:

 - split uvc_status_cleanup() into two parts, one on disconnect that
   unregisters and one on delete that frees.

 - move v4l2_device_unregister() and media_device_unregister() into
   the disconnect path.

[0]: https://lkml.org/lkml/2016/12/8/657

[Renamed uvc_input_cleanup() to uvc_input_unregister()]

Signed-off-by: Daniel Axtens <dja@axtens.net>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
[Sakari Ailus: Rebase on patch Revert "[media] media-device: dynamically allocate struct media_devnode"]
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/usb/uvc/uvc_driver.c | 13 +++++++++----
 drivers/media/usb/uvc/uvc_status.c | 12 ++++++++----
 drivers/media/usb/uvc/uvcvideo.h   |  1 +
 3 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 1044e13976b5..7add92f45329 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1859,11 +1859,7 @@ static void uvc_delete(struct kref *kref)
 	usb_put_intf(dev->intf);
 	usb_put_dev(dev->udev);
 
-	if (dev->vdev.dev)
-		v4l2_device_unregister(&dev->vdev);
 #ifdef CONFIG_MEDIA_CONTROLLER
-	if (media_devnode_is_registered(&dev->mdev.devnode))
-		media_device_unregister(&dev->mdev);
 	media_device_cleanup(&dev->mdev);
 #endif
 
@@ -1920,6 +1916,15 @@ static void uvc_unregister_video(struct uvc_device *dev)
 
 		uvc_debugfs_cleanup_stream(stream);
 	}
+
+	uvc_status_unregister(dev);
+
+	if (dev->vdev.dev)
+		v4l2_device_unregister(&dev->vdev);
+#ifdef CONFIG_MEDIA_CONTROLLER
+	if (media_devnode_is_registered(&dev->mdev.devnode))
+		media_device_unregister(&dev->mdev);
+#endif
 }
 
 int uvc_register_video_device(struct uvc_device *dev,
diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c
index 015be0886801..a78a88c710e2 100644
--- a/drivers/media/usb/uvc/uvc_status.c
+++ b/drivers/media/usb/uvc/uvc_status.c
@@ -73,7 +73,7 @@ static int uvc_input_init(struct uvc_device *dev)
 	return ret;
 }
 
-static void uvc_input_cleanup(struct uvc_device *dev)
+static void uvc_input_unregister(struct uvc_device *dev)
 {
 	if (dev->input)
 		input_unregister_device(dev->input);
@@ -90,7 +90,7 @@ static void uvc_input_report_key(struct uvc_device *dev, unsigned int code,
 
 #else
 #define uvc_input_init(dev)
-#define uvc_input_cleanup(dev)
+#define uvc_input_unregister(dev)
 #define uvc_input_report_key(dev, code, value)
 #endif /* CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV */
 
@@ -290,12 +290,16 @@ int uvc_status_init(struct uvc_device *dev)
 	return 0;
 }
 
-void uvc_status_cleanup(struct uvc_device *dev)
+void uvc_status_unregister(struct uvc_device *dev)
 {
 	usb_kill_urb(dev->int_urb);
+	uvc_input_unregister(dev);
+}
+
+void uvc_status_cleanup(struct uvc_device *dev)
+{
 	usb_free_urb(dev->int_urb);
 	kfree(dev->status);
-	uvc_input_cleanup(dev);
 }
 
 int uvc_status_start(struct uvc_device *dev, gfp_t flags)
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index ab8de60f5de2..6fb0a78b1b00 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -742,6 +742,7 @@ int uvc_register_video_device(struct uvc_device *dev,
 
 /* Status */
 int uvc_status_init(struct uvc_device *dev);
+void uvc_status_unregister(struct uvc_device *dev);
 void uvc_status_cleanup(struct uvc_device *dev);
 int uvc_status_start(struct uvc_device *dev, gfp_t flags);
 void uvc_status_stop(struct uvc_device *dev);
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 08/29] media: mc: Drop nop release callback
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (6 preceding siblings ...)
  2023-12-20 10:36 ` [PATCH v2 07/29] media: uvcvideo: Refactor teardown of uvc on USB disconnect Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2024-02-07  9:55   ` Laurent Pinchart
  2023-12-20 10:36 ` [PATCH v2 09/29] media: mc: Do not call cdev_device_del() if cdev_device_add() fails Sakari Ailus
                   ` (22 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

The release callback is only used to print a debug message. Drop it. (It
will be re-introduced later in a different form.)

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/mc/mc-device.c | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index d4553a3705f5..c0ea08a8fc31 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -566,11 +566,6 @@ static DEVICE_ATTR_RO(model);
  * Registration/unregistration
  */
 
-static void media_device_release(struct media_devnode *devnode)
-{
-	dev_dbg(devnode->parent, "Media device released\n");
-}
-
 static void __media_device_unregister_entity(struct media_entity *entity)
 {
 	struct media_device *mdev = entity->graph_obj.mdev;
@@ -718,7 +713,6 @@ int __must_check __media_device_register(struct media_device *mdev,
 	/* Register the device node. */
 	mdev->devnode.fops = &media_device_fops;
 	mdev->devnode.parent = mdev->dev;
-	mdev->devnode.release = media_device_release;
 
 	/* Set version 0 to indicate user-space that the graph is static */
 	mdev->topology_version = 0;
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 09/29] media: mc: Do not call cdev_device_del() if cdev_device_add() fails
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (7 preceding siblings ...)
  2023-12-20 10:36 ` [PATCH v2 08/29] media: mc: Drop nop release callback Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2024-02-07  9:57   ` Laurent Pinchart
  2023-12-20 10:36 ` [PATCH v2 10/29] media: mc: Delete character device early Sakari Ailus
                   ` (21 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

cdev_device_del() is the right function to remove a device when
cdev_device_add() succeeds. If it does not, however, put_device() needs to
be used instead. Fix this.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/mc/mc-devnode.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index ce93ab9be676..7e22938dfd81 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -254,7 +254,6 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
 
 cdev_add_error:
 	mutex_lock(&media_devnode_lock);
-	cdev_device_del(&devnode->cdev, &devnode->dev);
 	clear_bit(devnode->minor, media_devnode_nums);
 	mutex_unlock(&media_devnode_lock);
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 10/29] media: mc: Delete character device early
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (8 preceding siblings ...)
  2023-12-20 10:36 ` [PATCH v2 09/29] media: mc: Do not call cdev_device_del() if cdev_device_add() fails Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2024-02-07 10:08   ` Laurent Pinchart
  2023-12-20 10:36 ` [PATCH v2 11/29] media: mc: Split initialising and adding media devnode Sakari Ailus
                   ` (20 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

The parent of the character device related to the media devnode is the
media devnode. Thus the character device needs to be released before the
media devnode's release function. Move it to unregistering of the media
devnode, which mirrors adding the character device in conjunction with
registering the media devnode.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/mc/mc-devnode.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 7e22938dfd81..8bc7450ac144 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -51,9 +51,6 @@ static void media_devnode_release(struct device *cd)
 
 	mutex_lock(&media_devnode_lock);
 
-	/* Delete the cdev on this minor as well */
-	cdev_del(&devnode->cdev);
-
 	/* Mark device node number as free */
 	clear_bit(devnode->minor, media_devnode_nums);
 
@@ -270,6 +267,7 @@ void media_devnode_unregister(struct media_devnode *devnode)
 	clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
 	mutex_unlock(&media_devnode_lock);
 
+	cdev_del(&devnode->cdev);
 	device_unregister(&devnode->dev);
 }
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 11/29] media: mc: Split initialising and adding media devnode
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (9 preceding siblings ...)
  2023-12-20 10:36 ` [PATCH v2 10/29] media: mc: Delete character device early Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2024-02-07 10:46   ` Laurent Pinchart
  2023-12-20 10:36 ` [PATCH v2 12/29] media: mc: Shuffle functions around Sakari Ailus
                   ` (19 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

As registering a device node of an entity belonging to a media device
will require a reference to the struct device. Taking that reference is
only possible once the device has been initialised, which took place only
when it was registered. Split this in two, and initialise the device when
the media device is allocated.

Don't distribute the effects of these changes yet. Add media_device_get()
and media_device_put() first.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/mc/mc-device.c  | 18 +++++++++++++-----
 drivers/media/mc/mc-devnode.c | 17 ++++++++++-------
 include/media/media-devnode.h | 19 ++++++++++++++-----
 3 files changed, 37 insertions(+), 17 deletions(-)

diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index c0ea08a8fc31..ebf037cd5f4a 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -717,19 +717,26 @@ int __must_check __media_device_register(struct media_device *mdev,
 	/* Set version 0 to indicate user-space that the graph is static */
 	mdev->topology_version = 0;
 
+	media_devnode_init(&mdev->devnode);
+
 	ret = media_devnode_register(&mdev->devnode, owner);
 	if (ret < 0)
-		return ret;
+		goto out_put;
 
 	ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
-	if (ret < 0) {
-		media_devnode_unregister(&mdev->devnode);
-		return ret;
-	}
+	if (ret < 0)
+		goto out_unregister;
 
 	dev_dbg(mdev->dev, "Media device registered\n");
 
 	return 0;
+
+out_unregister:
+	media_devnode_unregister(&mdev->devnode);
+out_put:
+	put_device(&mdev->devnode.dev);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(__media_device_register);
 
@@ -803,6 +810,7 @@ void media_device_unregister(struct media_device *mdev)
 	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
 	dev_dbg(mdev->dev, "Media device unregistering\n");
 	media_devnode_unregister(&mdev->devnode);
+	put_device(&mdev->devnode.dev);
 }
 EXPORT_SYMBOL_GPL(media_device_unregister);
 
diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 8bc7450ac144..7b17419050fb 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -204,6 +204,11 @@ static const struct file_operations media_devnode_fops = {
 	.llseek = no_llseek,
 };
 
+void media_devnode_init(struct media_devnode *devnode)
+{
+	device_initialize(&devnode->dev);
+}
+
 int __must_check media_devnode_register(struct media_devnode *devnode,
 					struct module *owner)
 {
@@ -235,7 +240,6 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
 	if (devnode->parent)
 		devnode->dev.parent = devnode->parent;
 	dev_set_name(&devnode->dev, "media%d", devnode->minor);
-	device_initialize(&devnode->dev);
 
 	/* Part 3: Add the media and character devices */
 	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
@@ -267,14 +271,13 @@ void media_devnode_unregister(struct media_devnode *devnode)
 	clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
 	mutex_unlock(&media_devnode_lock);
 
-	cdev_del(&devnode->cdev);
-	device_unregister(&devnode->dev);
+	cdev_device_del(&devnode->cdev, &devnode->dev);
 }
 
 /*
  *	Initialise media for linux
  */
-static int __init media_devnode_init(void)
+static int __init media_devnode_module_init(void)
 {
 	int ret;
 
@@ -296,14 +299,14 @@ static int __init media_devnode_init(void)
 	return 0;
 }
 
-static void __exit media_devnode_exit(void)
+static void __exit media_devnode_module_exit(void)
 {
 	bus_unregister(&media_bus_type);
 	unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
 }
 
-subsys_initcall(media_devnode_init);
-module_exit(media_devnode_exit)
+subsys_initcall(media_devnode_module_init);
+module_exit(media_devnode_module_exit)
 
 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
 MODULE_DESCRIPTION("Device node registration for media drivers");
diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
index 1117d1dfd6bf..6d46c658be21 100644
--- a/include/media/media-devnode.h
+++ b/include/media/media-devnode.h
@@ -90,6 +90,17 @@ struct media_devnode {
 /* dev to media_devnode */
 #define to_media_devnode(cd) container_of(cd, struct media_devnode, dev)
 
+/**
+ * media_devnode_init - initialise a media devnode
+ *
+ * @devnode: struct media_devnode we want to initialise
+ *
+ * Initialise a media devnode. Note that after initialising the media
+ * devnode is refcounted. Releasing references to it may be done using
+ * put_device().
+ */
+void media_devnode_init(struct media_devnode *devnode);
+
 /**
  * media_devnode_register - register a media device node
  *
@@ -100,11 +111,9 @@ struct media_devnode {
  * with the kernel. An error is returned if no free minor number can be found,
  * or if the registration of the device node fails.
  *
- * Zero is returned on success.
- *
- * Note that if the media_devnode_register call fails, the release() callback of
- * the media_devnode structure is *not* called, so the caller is responsible for
- * freeing any data.
+ * Zero is returned on success. Note that in case
+ * media_devnode_register() fails, the caller is responsible for
+ * releasing the reference to the device using put_device().
  */
 int __must_check media_devnode_register(struct media_devnode *devnode,
 					struct module *owner);
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 12/29] media: mc: Shuffle functions around
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (10 preceding siblings ...)
  2023-12-20 10:36 ` [PATCH v2 11/29] media: mc: Split initialising and adding media devnode Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2024-02-07 10:47   ` Laurent Pinchart
  2023-12-20 10:36 ` [PATCH v2 13/29] media: mc: Initialise media devnode in media_device_init() Sakari Ailus
                   ` (18 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

As the call paths of the functions in question will change, move them
around in anticipation of that. No other changes.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/mc/mc-device.c | 54 ++++++++++++++++++------------------
 1 file changed, 27 insertions(+), 27 deletions(-)

diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index ebf037cd5f4a..44685ab6a450 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -673,6 +673,33 @@ void media_device_unregister_entity(struct media_entity *entity)
 }
 EXPORT_SYMBOL_GPL(media_device_unregister_entity);
 
+void media_device_register_entity_notify(struct media_device *mdev,
+					struct media_entity_notify *nptr)
+{
+	mutex_lock(&mdev->graph_mutex);
+	list_add_tail(&nptr->list, &mdev->entity_notify);
+	mutex_unlock(&mdev->graph_mutex);
+}
+EXPORT_SYMBOL_GPL(media_device_register_entity_notify);
+
+/*
+ * Note: Should be called with mdev->lock held.
+ */
+static void __media_device_unregister_entity_notify(struct media_device *mdev,
+					struct media_entity_notify *nptr)
+{
+	list_del(&nptr->list);
+}
+
+void media_device_unregister_entity_notify(struct media_device *mdev,
+					struct media_entity_notify *nptr)
+{
+	mutex_lock(&mdev->graph_mutex);
+	__media_device_unregister_entity_notify(mdev, nptr);
+	mutex_unlock(&mdev->graph_mutex);
+}
+EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify);
+
 void media_device_init(struct media_device *mdev)
 {
 	INIT_LIST_HEAD(&mdev->entities);
@@ -740,33 +767,6 @@ int __must_check __media_device_register(struct media_device *mdev,
 }
 EXPORT_SYMBOL_GPL(__media_device_register);
 
-void media_device_register_entity_notify(struct media_device *mdev,
-					struct media_entity_notify *nptr)
-{
-	mutex_lock(&mdev->graph_mutex);
-	list_add_tail(&nptr->list, &mdev->entity_notify);
-	mutex_unlock(&mdev->graph_mutex);
-}
-EXPORT_SYMBOL_GPL(media_device_register_entity_notify);
-
-/*
- * Note: Should be called with mdev->lock held.
- */
-static void __media_device_unregister_entity_notify(struct media_device *mdev,
-					struct media_entity_notify *nptr)
-{
-	list_del(&nptr->list);
-}
-
-void media_device_unregister_entity_notify(struct media_device *mdev,
-					struct media_entity_notify *nptr)
-{
-	mutex_lock(&mdev->graph_mutex);
-	__media_device_unregister_entity_notify(mdev, nptr);
-	mutex_unlock(&mdev->graph_mutex);
-}
-EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify);
-
 void media_device_unregister(struct media_device *mdev)
 {
 	struct media_entity *entity;
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 13/29] media: mc: Initialise media devnode in media_device_init()
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (11 preceding siblings ...)
  2023-12-20 10:36 ` [PATCH v2 12/29] media: mc: Shuffle functions around Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2024-02-07 10:51   ` Laurent Pinchart
  2023-12-20 10:36 ` [PATCH v2 14/29] media: mc: Refactor media devnode minor clearing Sakari Ailus
                   ` (17 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

Call media_devnode_init() from media_device_init(). This has the effect of
creating a struct device for the media_devnode before it is registered,
making it possible to obtain a reference to it for e.g. video devices.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/mc/mc-device.c | 21 +++++++--------------
 1 file changed, 7 insertions(+), 14 deletions(-)

diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index 44685ab6a450..e6ac9b066524 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -711,8 +711,8 @@ void media_device_init(struct media_device *mdev)
 	mutex_init(&mdev->req_queue_mutex);
 	mutex_init(&mdev->graph_mutex);
 	ida_init(&mdev->entity_internal_idx);
-
 	atomic_set(&mdev->request_id, 0);
+	media_devnode_init(&mdev->devnode);
 
 	if (!*mdev->bus_info)
 		media_set_bus_info(mdev->bus_info, sizeof(mdev->bus_info),
@@ -729,6 +729,7 @@ void media_device_cleanup(struct media_device *mdev)
 	media_graph_walk_cleanup(&mdev->pm_count_walk);
 	mutex_destroy(&mdev->graph_mutex);
 	mutex_destroy(&mdev->req_queue_mutex);
+	put_device(&mdev->devnode.dev);
 }
 EXPORT_SYMBOL_GPL(media_device_cleanup);
 
@@ -744,26 +745,19 @@ int __must_check __media_device_register(struct media_device *mdev,
 	/* Set version 0 to indicate user-space that the graph is static */
 	mdev->topology_version = 0;
 
-	media_devnode_init(&mdev->devnode);
-
 	ret = media_devnode_register(&mdev->devnode, owner);
 	if (ret < 0)
-		goto out_put;
+		return ret;
 
 	ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
-	if (ret < 0)
-		goto out_unregister;
+	if (ret < 0) {
+		media_devnode_unregister(&mdev->devnode);
+		return ret;
+	}
 
 	dev_dbg(mdev->dev, "Media device registered\n");
 
 	return 0;
-
-out_unregister:
-	media_devnode_unregister(&mdev->devnode);
-out_put:
-	put_device(&mdev->devnode.dev);
-
-	return ret;
 }
 EXPORT_SYMBOL_GPL(__media_device_register);
 
@@ -810,7 +804,6 @@ void media_device_unregister(struct media_device *mdev)
 	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
 	dev_dbg(mdev->dev, "Media device unregistering\n");
 	media_devnode_unregister(&mdev->devnode);
-	put_device(&mdev->devnode.dev);
 }
 EXPORT_SYMBOL_GPL(media_device_unregister);
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 14/29] media: mc: Refactor media devnode minor clearing
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (12 preceding siblings ...)
  2023-12-20 10:36 ` [PATCH v2 13/29] media: mc: Initialise media devnode in media_device_init() Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2024-02-05 14:46   ` Hans Verkuil
  2024-02-07 10:53   ` Laurent Pinchart
  2023-12-20 10:36 ` [PATCH v2 15/29] media: mc: Unassign minor only if it has been assigned Sakari Ailus
                   ` (16 subsequent siblings)
  30 siblings, 2 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

Refactor clearing media devnode minor bit in media devnode bitmap. Note
that number is used instead of struct media_devnode as argument since the
minor number will also be stored in a different structure soon.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/mc/mc-devnode.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 7b17419050fb..717408791a7c 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -44,17 +44,22 @@ static dev_t media_dev_t;
 static DEFINE_MUTEX(media_devnode_lock);
 static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
 
-/* Called when the last user of the media device exits. */
-static void media_devnode_release(struct device *cd)
+static void media_devnode_free_minor(unsigned int minor)
 {
-	struct media_devnode *devnode = to_media_devnode(cd);
-
 	mutex_lock(&media_devnode_lock);
 
 	/* Mark device node number as free */
-	clear_bit(devnode->minor, media_devnode_nums);
+	clear_bit(minor, media_devnode_nums);
 
 	mutex_unlock(&media_devnode_lock);
+}
+
+/* Called when the last user of the media device exits. */
+static void media_devnode_release(struct device *cd)
+{
+	struct media_devnode *devnode = to_media_devnode(cd);
+
+	media_devnode_free_minor(devnode->minor);
 
 	/* Release media_devnode and perform other cleanups as needed. */
 	if (devnode->release)
@@ -254,9 +259,7 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
 	return 0;
 
 cdev_add_error:
-	mutex_lock(&media_devnode_lock);
-	clear_bit(devnode->minor, media_devnode_nums);
-	mutex_unlock(&media_devnode_lock);
+	media_devnode_free_minor(devnode->minor);
 
 	return ret;
 }
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 15/29] media: mc: Unassign minor only if it has been assigned
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (13 preceding siblings ...)
  2023-12-20 10:36 ` [PATCH v2 14/29] media: mc: Refactor media devnode minor clearing Sakari Ailus
@ 2023-12-20 10:36 ` Sakari Ailus
  2024-02-05 14:48   ` Hans Verkuil
  2024-02-07 10:58   ` Laurent Pinchart
  2023-12-20 10:37 ` [PATCH v2 16/29] media: mc: Refcount the media device Sakari Ailus
                   ` (15 subsequent siblings)
  30 siblings, 2 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:36 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

Assign the media device minor to -1 if it has not been previously
assigned. There's no dependence to this yes but it will be required by
patch "media: mc: Implement best effort media device removal safety sans
refcount" soon.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/mc/mc-devnode.c | 9 ++++++++-
 include/media/media-devnode.h | 2 +-
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 717408791a7c..5057c48f8870 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -59,7 +59,8 @@ static void media_devnode_release(struct device *cd)
 {
 	struct media_devnode *devnode = to_media_devnode(cd);
 
-	media_devnode_free_minor(devnode->minor);
+	if (devnode->minor != -1)
+		media_devnode_free_minor(devnode->minor);
 
 	/* Release media_devnode and perform other cleanups as needed. */
 	if (devnode->release)
@@ -212,6 +213,7 @@ static const struct file_operations media_devnode_fops = {
 void media_devnode_init(struct media_devnode *devnode)
 {
 	device_initialize(&devnode->dev);
+	devnode->minor = -1;
 }
 
 int __must_check media_devnode_register(struct media_devnode *devnode,
@@ -220,6 +222,9 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
 	int minor;
 	int ret;
 
+	if (devnode->minor != -1)
+		return -EINVAL;
+
 	/* Part 1: Find a free minor number */
 	mutex_lock(&media_devnode_lock);
 	minor = find_first_zero_bit(media_devnode_nums, MEDIA_NUM_DEVICES);
@@ -261,6 +266,8 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
 cdev_add_error:
 	media_devnode_free_minor(devnode->minor);
 
+	devnode->minor = -1;
+
 	return ret;
 }
 
diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
index 6d46c658be21..d050f54f252a 100644
--- a/include/media/media-devnode.h
+++ b/include/media/media-devnode.h
@@ -60,7 +60,7 @@ struct media_file_operations {
  * @dev:	pointer to struct &device containing the media controller device
  * @cdev:	struct cdev pointer character device
  * @parent:	parent device
- * @minor:	device node minor number
+ * @minor:	device node minor number, -1 if unassigned
  * @flags:	flags, combination of the ``MEDIA_FLAG_*`` constants
  * @release:	release callback called at the end of ``media_devnode_release()``
  *		routine at media-device.c.
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 16/29] media: mc: Refcount the media device
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (14 preceding siblings ...)
  2023-12-20 10:36 ` [PATCH v2 15/29] media: mc: Unassign minor only if it has been assigned Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2024-02-07 11:08   ` Laurent Pinchart
  2023-12-20 10:37 ` [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device Sakari Ailus
                   ` (14 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

As the struct media_device embeds struct media_devnode, the lifetime of
that object must be that same than that of the media_device.

References are obtained by media_device_get() and released by
media_device_put(). In order to use refcounting, the driver must set the
release callback before calling media_device_init() on the media device.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/mc/mc-device.c  | 36 +++++++++++++++++++++++++++++------
 drivers/media/mc/mc-devnode.c |  6 +++++-
 include/media/media-device.h  | 28 +++++++++++++++++++++++++++
 3 files changed, 63 insertions(+), 7 deletions(-)

diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index e6ac9b066524..bbc233e726d2 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -700,6 +700,31 @@ void media_device_unregister_entity_notify(struct media_device *mdev,
 }
 EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify);
 
+static void __media_device_release(struct media_device *mdev)
+{
+	dev_dbg(mdev->dev, "Media device released\n");
+
+	ida_destroy(&mdev->entity_internal_idx);
+	mdev->entity_internal_idx_max = 0;
+	media_graph_walk_cleanup(&mdev->pm_count_walk);
+	mutex_destroy(&mdev->graph_mutex);
+	mutex_destroy(&mdev->req_queue_mutex);
+}
+
+static void media_device_release(struct media_devnode *devnode)
+{
+	struct media_device *mdev = to_media_device(devnode);
+
+	if (mdev->ops && mdev->ops->release) {
+		/*
+		 * If release op isn't set, __media_device_release() is called
+		 * via media_device_cleanup().
+		 */
+		__media_device_release(mdev);
+		mdev->ops->release(mdev);
+	}
+}
+
 void media_device_init(struct media_device *mdev)
 {
 	INIT_LIST_HEAD(&mdev->entities);
@@ -712,6 +737,8 @@ void media_device_init(struct media_device *mdev)
 	mutex_init(&mdev->graph_mutex);
 	ida_init(&mdev->entity_internal_idx);
 	atomic_set(&mdev->request_id, 0);
+
+	mdev->devnode.release = media_device_release;
 	media_devnode_init(&mdev->devnode);
 
 	if (!*mdev->bus_info)
@@ -724,12 +751,9 @@ EXPORT_SYMBOL_GPL(media_device_init);
 
 void media_device_cleanup(struct media_device *mdev)
 {
-	ida_destroy(&mdev->entity_internal_idx);
-	mdev->entity_internal_idx_max = 0;
-	media_graph_walk_cleanup(&mdev->pm_count_walk);
-	mutex_destroy(&mdev->graph_mutex);
-	mutex_destroy(&mdev->req_queue_mutex);
-	put_device(&mdev->devnode.dev);
+	WARN_ON(mdev->ops && mdev->ops->release);
+	__media_device_release(mdev);
+	media_device_put(mdev);
 }
 EXPORT_SYMBOL_GPL(media_device_cleanup);
 
diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 5057c48f8870..4ea05e42dafb 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -59,6 +59,10 @@ static void media_devnode_release(struct device *cd)
 {
 	struct media_devnode *devnode = to_media_devnode(cd);
 
+	/* If the devnode has a ref, it is simply released by the user. */
+	if (devnode->ref)
+		return;
+
 	if (devnode->minor != -1)
 		media_devnode_free_minor(devnode->minor);
 
@@ -213,6 +217,7 @@ static const struct file_operations media_devnode_fops = {
 void media_devnode_init(struct media_devnode *devnode)
 {
 	device_initialize(&devnode->dev);
+	devnode->dev.release = media_devnode_release;
 	devnode->minor = -1;
 }
 
@@ -246,7 +251,6 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
 
 	devnode->dev.bus = &media_bus_type;
 	devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
-	devnode->dev.release = media_devnode_release;
 	if (devnode->parent)
 		devnode->dev.parent = devnode->parent;
 	dev_set_name(&devnode->dev, "media%d", devnode->minor);
diff --git a/include/media/media-device.h b/include/media/media-device.h
index fb0855b217ce..c6816be0eee8 100644
--- a/include/media/media-device.h
+++ b/include/media/media-device.h
@@ -62,6 +62,7 @@ struct media_entity_notify {
  *	       request (and thus the buffer) must be available to the driver.
  *	       And once a buffer is queued, then the driver can complete
  *	       or delete objects from the request before req_queue exits.
+ * @release: Release the resources of the media device.
  */
 struct media_device_ops {
 	int (*link_notify)(struct media_link *link, u32 flags,
@@ -70,6 +71,7 @@ struct media_device_ops {
 	void (*req_free)(struct media_request *req);
 	int (*req_validate)(struct media_request *req);
 	void (*req_queue)(struct media_request *req);
+	void (*release)(struct media_device *mdev);
 };
 
 /**
@@ -219,6 +221,30 @@ struct usb_device;
  */
 void media_device_init(struct media_device *mdev);
 
+/**
+ * media_device_get() - Get a reference to a media device
+ *
+ * @mdev: media device
+ */
+#define media_device_get(mdev)						\
+	do {								\
+		dev_dbg((mdev)->dev, "%s: get media device %s\n",	\
+			__func__, (mdev)->bus_info);			\
+		get_device(&(mdev)->devnode.dev);			\
+	} while (0)
+
+/**
+ * media_device_put() - Put a reference to a media device
+ *
+ * @mdev: media device
+ */
+#define media_device_put(mdev)						\
+	do {								\
+		dev_dbg((mdev)->dev, "%s: put media device %s\n",	\
+			__func__, (mdev)->bus_info);			\
+		put_device(&(mdev)->devnode.dev);			\
+	} while (0)
+
 /**
  * media_device_cleanup() - Cleanups a media device element
  *
@@ -432,6 +458,8 @@ void __media_device_usb_init(struct media_device *mdev,
 			     const char *driver_name);
 
 #else
+#define media_device_get(mdev) do { } while (0)
+#define media_device_put(mdev) do { } while (0)
 static inline int media_device_register(struct media_device *mdev)
 {
 	return 0;
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (15 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 16/29] media: mc: Refcount the media device Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2024-02-05 14:56   ` Hans Verkuil
  2023-12-20 10:37 ` [PATCH v2 18/29] media: mc: Postpone graph object removal until free Sakari Ailus
                   ` (13 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

The video device depends on the existence of its media device --- if there
is one. Acquire a reference to it.

Note that when the media device release callback is used, then the V4L2
device release callback is ignored and a warning is issued if both are
set.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/v4l2-core/v4l2-dev.c | 51 ++++++++++++++++++++----------
 1 file changed, 34 insertions(+), 17 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index d13954bd31fd..c1e4995eaf5c 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -176,6 +176,11 @@ static void v4l2_device_release(struct device *cd)
 {
 	struct video_device *vdev = to_video_device(cd);
 	struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
+	bool v4l2_dev_has_release = v4l2_dev->release;
+#ifdef CONFIG_MEDIA_CONTROLLER
+	struct media_device *mdev = v4l2_dev->mdev;
+	bool mdev_has_release = mdev && mdev->ops && mdev->ops->release;
+#endif
 
 	mutex_lock(&videodev_lock);
 	if (WARN_ON(video_devices[vdev->minor] != vdev)) {
@@ -198,8 +203,8 @@ static void v4l2_device_release(struct device *cd)
 
 	mutex_unlock(&videodev_lock);
 
-#if defined(CONFIG_MEDIA_CONTROLLER)
-	if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
+#ifdef CONFIG_MEDIA_CONTROLLER
+	if (mdev && vdev->vfl_dir != VFL_DIR_M2M) {
 		/* Remove interfaces and interface links */
 		media_devnode_remove(vdev->intf_devnode);
 		if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
@@ -207,23 +212,31 @@ static void v4l2_device_release(struct device *cd)
 	}
 #endif
 
-	/* Do not call v4l2_device_put if there is no release callback set.
-	 * Drivers that have no v4l2_device release callback might free the
-	 * v4l2_dev instance in the video_device release callback below, so we
-	 * must perform this check here.
-	 *
-	 * TODO: In the long run all drivers that use v4l2_device should use the
-	 * v4l2_device release callback. This check will then be unnecessary.
-	 */
-	if (v4l2_dev->release == NULL)
-		v4l2_dev = NULL;
-
 	/* Release video_device and perform other
 	   cleanups as needed. */
 	vdev->release(vdev);
 
-	/* Decrease v4l2_device refcount */
-	if (v4l2_dev)
+#ifdef CONFIG_MEDIA_CONTROLLER
+	if (mdev)
+		media_device_put(mdev);
+
+	/*
+	 * Generally both struct media_device and struct v4l2_device are
+	 * embedded in the same driver's context struct so having a release
+	 * callback in both is a bug.
+	 */
+	WARN_ON(v4l2_dev_has_release && mdev_has_release);
+#endif
+
+	/*
+	 * Decrease v4l2_device refcount, but only if the media device doesn't
+	 * have a release callback.
+	 */
+	if (v4l2_dev_has_release
+#ifdef CONFIG_MEDIA_CONTROLLER
+	    && !mdev_has_release
+#endif
+	    )
 		v4l2_device_put(v4l2_dev);
 }
 
@@ -792,11 +805,14 @@ static int video_register_media_controller(struct video_device *vdev)
 	u32 intf_type;
 	int ret;
 
-	/* Memory-to-memory devices are more complex and use
+	/*
+	 * Memory-to-memory devices are more complex and use
 	 * their own function to register its mc entities.
 	 */
-	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M)
+	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M) {
+		media_device_get(vdev->v4l2_dev->mdev);
 		return 0;
+	}
 
 	vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
 	vdev->entity.function = MEDIA_ENT_F_UNKNOWN;
@@ -875,6 +891,7 @@ static int video_register_media_controller(struct video_device *vdev)
 
 	/* FIXME: how to create the other interface links? */
 
+	media_device_get(vdev->v4l2_dev->mdev);
 #endif
 	return 0;
 }
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 18/29] media: mc: Postpone graph object removal until free
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (16 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2024-02-07 14:18   ` Laurent Pinchart
  2023-12-20 10:37 ` [PATCH v2 19/29] media: omap3isp: Release the isp device struct by media device callback Sakari Ailus
                   ` (12 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

The media device itself will be unregistered based on it being unbound and
driver's remove callback being called. The graph objects themselves may
still be in use; rely on the media device release callback to release
them.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/mc/mc-device.c | 53 +++++++++++++++++-------------------
 1 file changed, 25 insertions(+), 28 deletions(-)

diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index bbc233e726d2..10426c2796b6 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -702,8 +702,33 @@ EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify);
 
 static void __media_device_release(struct media_device *mdev)
 {
+	struct media_entity *entity;
+	struct media_entity *next;
+	struct media_interface *intf, *tmp_intf;
+	struct media_entity_notify *notify, *nextp;
+
 	dev_dbg(mdev->dev, "Media device released\n");
 
+	/* Remove all entities from the media device */
+	list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
+		__media_device_unregister_entity(entity);
+
+	/* Remove all entity_notify callbacks from the media device */
+	list_for_each_entry_safe(notify, nextp, &mdev->entity_notify, list)
+		__media_device_unregister_entity_notify(mdev, notify);
+
+	/* Remove all interfaces from the media device */
+	list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces,
+				 graph_obj.list) {
+		/*
+		 * Unlink the interface, but don't free it here; the
+		 * module which created it is responsible for freeing
+		 * it
+		 */
+		__media_remove_intf_links(intf);
+		media_gobj_destroy(&intf->graph_obj);
+	}
+
 	ida_destroy(&mdev->entity_internal_idx);
 	mdev->entity_internal_idx_max = 0;
 	media_graph_walk_cleanup(&mdev->pm_count_walk);
@@ -787,42 +812,14 @@ EXPORT_SYMBOL_GPL(__media_device_register);
 
 void media_device_unregister(struct media_device *mdev)
 {
-	struct media_entity *entity;
-	struct media_entity *next;
-	struct media_interface *intf, *tmp_intf;
-	struct media_entity_notify *notify, *nextp;
-
 	if (mdev == NULL)
 		return;
 
 	mutex_lock(&mdev->graph_mutex);
-
-	/* Check if mdev was ever registered at all */
 	if (!media_devnode_is_registered(&mdev->devnode)) {
 		mutex_unlock(&mdev->graph_mutex);
 		return;
 	}
-
-	/* Remove all entities from the media device */
-	list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
-		__media_device_unregister_entity(entity);
-
-	/* Remove all entity_notify callbacks from the media device */
-	list_for_each_entry_safe(notify, nextp, &mdev->entity_notify, list)
-		__media_device_unregister_entity_notify(mdev, notify);
-
-	/* Remove all interfaces from the media device */
-	list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces,
-				 graph_obj.list) {
-		/*
-		 * Unlink the interface, but don't free it here; the
-		 * module which created it is responsible for freeing
-		 * it
-		 */
-		__media_remove_intf_links(intf);
-		media_gobj_destroy(&intf->graph_obj);
-	}
-
 	mutex_unlock(&mdev->graph_mutex);
 
 	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 19/29] media: omap3isp: Release the isp device struct by media device callback
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (17 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 18/29] media: mc: Postpone graph object removal until free Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2024-02-07 14:23   ` Laurent Pinchart
  2023-12-20 10:37 ` [PATCH v2 20/29] media: ipu3-cio2: Call v4l2_device_unregister() earlier Sakari Ailus
                   ` (11 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

Use the media device release callback to release the isp device's data
structure. This approach has the benefit of not releasing memory which may
still be accessed through open file handles whilst the isp driver is being
unbound.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/platform/ti/omap3isp/isp.c | 24 +++++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/drivers/media/platform/ti/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c
index 1cda23244c7b..ef6a781d6da1 100644
--- a/drivers/media/platform/ti/omap3isp/isp.c
+++ b/drivers/media/platform/ti/omap3isp/isp.c
@@ -649,8 +649,11 @@ static irqreturn_t isp_isr(int irq, void *_isp)
 	return IRQ_HANDLED;
 }
 
+static void isp_release(struct media_device *mdev);
+
 static const struct media_device_ops isp_media_ops = {
 	.link_notify = v4l2_pipeline_link_notify,
+	.release = isp_release,
 };
 
 /* -----------------------------------------------------------------------------
@@ -1607,7 +1610,6 @@ static void isp_unregister_entities(struct isp_device *isp)
 	omap3isp_stat_unregister_entities(&isp->isp_hist);
 
 	v4l2_device_unregister(&isp->v4l2_dev);
-	media_device_cleanup(&isp->media_dev);
 }
 
 static int isp_link_entity(
@@ -1955,6 +1957,19 @@ static void isp_detach_iommu(struct isp_device *isp)
 #endif
 }
 
+static void isp_release(struct media_device *mdev)
+{
+	struct isp_device *isp =
+		container_of(mdev, struct isp_device, media_dev);
+
+	isp_cleanup_modules(isp);
+
+	media_entity_enum_cleanup(&isp->crashed);
+	v4l2_async_nf_cleanup(&isp->notifier);
+
+	kfree(isp);
+}
+
 static int isp_attach_iommu(struct isp_device *isp)
 {
 #ifdef CONFIG_ARM_DMA_USE_IOMMU
@@ -2004,16 +2019,15 @@ static void isp_remove(struct platform_device *pdev)
 	v4l2_async_nf_unregister(&isp->notifier);
 	v4l2_async_nf_cleanup(&isp->notifier);
 	isp_unregister_entities(isp);
-	isp_cleanup_modules(isp);
+
 	isp_xclk_cleanup(isp);
 
 	__omap3isp_get(isp, false);
 	isp_detach_iommu(isp);
 	__omap3isp_put(isp, false);
 
-	media_entity_enum_cleanup(&isp->crashed);
-
-	kfree(isp);
+	/* May release isp immediately */
+	media_device_put(&isp->media_dev);
 }
 
 enum isp_of_phy {
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 20/29] media: ipu3-cio2: Call v4l2_device_unregister() earlier
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (18 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 19/29] media: omap3isp: Release the isp device struct by media device callback Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2024-02-07 14:24   ` Laurent Pinchart
  2023-12-20 10:37 ` [PATCH v2 21/29] media: ipu3-cio2: Request IRQ earlier Sakari Ailus
                   ` (10 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

v4l2_device_unregister() unregisters V4L2 sub-device nodes among other
things. Call it before releasing memory and other resources.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/pci/intel/ipu3/ipu3-cio2.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index 5d3b0ffd3d08..da82d09b46ab 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -1827,11 +1827,11 @@ static void cio2_pci_remove(struct pci_dev *pci_dev)
 	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
 
 	media_device_unregister(&cio2->media_dev);
+	v4l2_device_unregister(&cio2->v4l2_dev);
 	v4l2_async_nf_unregister(&cio2->notifier);
 	v4l2_async_nf_cleanup(&cio2->notifier);
 	cio2_queues_exit(cio2);
 	cio2_fbpt_exit_dummy(cio2);
-	v4l2_device_unregister(&cio2->v4l2_dev);
 	media_device_cleanup(&cio2->media_dev);
 	mutex_destroy(&cio2->lock);
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 21/29] media: ipu3-cio2: Request IRQ earlier
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (19 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 20/29] media: ipu3-cio2: Call v4l2_device_unregister() earlier Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2024-02-05 14:58   ` Hans Verkuil
  2023-12-20 10:37 ` [PATCH v2 22/29] media: ipu3-cio2: Release the cio2 device context by media device callback Sakari Ailus
                   ` (9 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

Call devm_request_irq() before registering the async notifier, as otherwise
it would be possible to use the device before the interrupts could be
deliveted to the driver.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/pci/intel/ipu3/ipu3-cio2.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index da82d09b46ab..3222ec5b8345 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -1789,11 +1789,6 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
 
 	v4l2_async_nf_init(&cio2->notifier, &cio2->v4l2_dev);
 
-	/* Register notifier for subdevices we care */
-	r = cio2_parse_firmware(cio2);
-	if (r)
-		goto fail_clean_notifier;
-
 	r = devm_request_irq(dev, pci_dev->irq, cio2_irq, IRQF_SHARED,
 			     CIO2_NAME, cio2);
 	if (r) {
@@ -1801,6 +1796,11 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
 		goto fail_clean_notifier;
 	}
 
+	/* Register notifier for subdevices we care */
+	r = cio2_parse_firmware(cio2);
+	if (r)
+		goto fail_clean_notifier;
+
 	pm_runtime_put_noidle(dev);
 	pm_runtime_allow(dev);
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 22/29] media: ipu3-cio2: Release the cio2 device context by media device callback
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (20 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 21/29] media: ipu3-cio2: Request IRQ earlier Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2024-02-07 14:33   ` Laurent Pinchart
  2023-12-20 10:37 ` [PATCH v2 23/29] media: vimc: Release resources on media device release Sakari Ailus
                   ` (8 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

Use the media device release callback to release the cio2 device's data
structure. This approach has the benefit of not releasing memory which may
still be accessed through open file handles whilst the ipu3-cio2 driver is
being unbound.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 drivers/media/pci/intel/ipu3/ipu3-cio2.c | 58 ++++++++++++++++--------
 1 file changed, 40 insertions(+), 18 deletions(-)

diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index 3222ec5b8345..bff66e6d3b1e 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -238,9 +238,15 @@ static int cio2_fbpt_init(struct cio2_device *cio2, struct cio2_queue *q)
 	return 0;
 }
 
-static void cio2_fbpt_exit(struct cio2_queue *q, struct device *dev)
+static int cio2_fbpt_exit(struct cio2_queue *q, struct device *dev)
 {
+	if (!q->fbpt)
+		return -ENOENT;
+
 	dma_free_coherent(dev, CIO2_FBPT_SIZE, q->fbpt, q->fbpt_bus_addr);
+	q->fbpt = NULL;
+
+	return 0;
 }
 
 /**************** CSI2 hardware setup ****************/
@@ -1643,13 +1649,13 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q)
 
 static void cio2_queue_exit(struct cio2_device *cio2, struct cio2_queue *q)
 {
-	vb2_video_unregister_device(&q->vdev);
 	media_entity_cleanup(&q->vdev.entity);
 	v4l2_device_unregister_subdev(&q->subdev);
 	media_entity_cleanup(&q->subdev.entity);
-	cio2_fbpt_exit(q, &cio2->pci_dev->dev);
-	mutex_destroy(&q->subdev_lock);
-	mutex_destroy(&q->lock);
+	if (!cio2_fbpt_exit(q, &cio2->pci_dev->dev)) {
+		mutex_destroy(&q->subdev_lock);
+		mutex_destroy(&q->lock);
+	}
 }
 
 static int cio2_queues_init(struct cio2_device *cio2)
@@ -1695,6 +1701,23 @@ static int cio2_check_fwnode_graph(struct fwnode_handle *fwnode)
 	return cio2_check_fwnode_graph(fwnode->secondary);
 }
 
+static void cio2_media_release(struct media_device *mdev)
+{
+	struct cio2_device *cio2 =
+		container_of(mdev, struct cio2_device, media_dev);
+
+	v4l2_async_nf_cleanup(&cio2->notifier);
+	cio2_queues_exit(cio2);
+	cio2_fbpt_exit_dummy(cio2);
+	mutex_destroy(&cio2->lock);
+
+	kfree(cio2);
+}
+
+static const struct media_device_ops cio2_mdev_ops = {
+	.release = cio2_media_release,
+};
+
 /**************** PCI interface ****************/
 
 static int cio2_pci_probe(struct pci_dev *pci_dev,
@@ -1722,7 +1745,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
 			return r;
 	}
 
-	cio2 = devm_kzalloc(dev, sizeof(*cio2), GFP_KERNEL);
+	cio2 = kzalloc(sizeof(*cio2), GFP_KERNEL);
 	if (!cio2)
 		return -ENOMEM;
 	cio2->pci_dev = pci_dev;
@@ -1767,6 +1790,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
 	mutex_init(&cio2->lock);
 
 	cio2->media_dev.dev = dev;
+	cio2->media_dev.ops = &cio2_mdev_ops;
 	strscpy(cio2->media_dev.model, CIO2_DEVICE_NAME,
 		sizeof(cio2->media_dev.model));
 	cio2->media_dev.hw_revision = 0;
@@ -1774,7 +1798,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
 	media_device_init(&cio2->media_dev);
 	r = media_device_register(&cio2->media_dev);
 	if (r < 0)
-		goto fail_mutex_destroy;
+		goto fail_media_device_put;
 
 	cio2->v4l2_dev.mdev = &cio2->media_dev;
 	r = v4l2_device_register(dev, &cio2->v4l2_dev);
@@ -1808,35 +1832,33 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
 
 fail_clean_notifier:
 	v4l2_async_nf_unregister(&cio2->notifier);
-	v4l2_async_nf_cleanup(&cio2->notifier);
-	cio2_queues_exit(cio2);
+
 fail_v4l2_device_unregister:
 	v4l2_device_unregister(&cio2->v4l2_dev);
+
 fail_media_device_unregister:
 	media_device_unregister(&cio2->media_dev);
-	media_device_cleanup(&cio2->media_dev);
-fail_mutex_destroy:
-	mutex_destroy(&cio2->lock);
-	cio2_fbpt_exit_dummy(cio2);
 
+fail_media_device_put:
+	media_device_put(&cio2->media_dev);
 	return r;
 }
 
 static void cio2_pci_remove(struct pci_dev *pci_dev)
 {
 	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
+	unsigned int i;
 
 	media_device_unregister(&cio2->media_dev);
+	for (i = 0; i < CIO2_QUEUES; i++)
+		vb2_video_unregister_device(&cio2->queue[i].vdev);
 	v4l2_device_unregister(&cio2->v4l2_dev);
 	v4l2_async_nf_unregister(&cio2->notifier);
-	v4l2_async_nf_cleanup(&cio2->notifier);
-	cio2_queues_exit(cio2);
-	cio2_fbpt_exit_dummy(cio2);
-	media_device_cleanup(&cio2->media_dev);
-	mutex_destroy(&cio2->lock);
 
 	pm_runtime_forbid(&pci_dev->dev);
 	pm_runtime_get_noresume(&pci_dev->dev);
+
+	media_device_put(&cio2->media_dev);
 }
 
 static int __maybe_unused cio2_runtime_suspend(struct device *dev)
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 23/29] media: vimc: Release resources on media device release
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (21 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 22/29] media: ipu3-cio2: Release the cio2 device context by media device callback Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2024-02-05 15:02   ` Hans Verkuil
  2023-12-20 10:37 ` [PATCH v2 24/29] media: Documentation: Document how Media device resources are released Sakari Ailus
                   ` (7 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

Release all the resources when the media device is related, moving away
form the struct v4l2_device used for that purpose.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/test-drivers/vimc/vimc-core.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c
index af127476e920..3e59f8c256c7 100644
--- a/drivers/media/test-drivers/vimc/vimc-core.c
+++ b/drivers/media/test-drivers/vimc/vimc-core.c
@@ -264,13 +264,12 @@ static int vimc_add_subdevs(struct vimc_device *vimc)
 	return 0;
 }
 
-static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev)
+static void vimc_mdev_release(struct media_device *mdev)
 {
 	struct vimc_device *vimc =
-		container_of(v4l2_dev, struct vimc_device, v4l2_dev);
+		container_of_const(mdev, struct vimc_device, mdev);
 
 	vimc_release_subdevs(vimc);
-	media_device_cleanup(&vimc->mdev);
 	kfree(vimc->ent_devs);
 	kfree(vimc);
 }
@@ -336,6 +335,10 @@ static int vimc_register_devices(struct vimc_device *vimc)
 	return ret;
 }
 
+static const struct media_device_ops vimc_mdev_ops = {
+	.release = vimc_mdev_release,
+};
+
 static int vimc_probe(struct platform_device *pdev)
 {
 	const struct font_desc *font = find_font("VGA8x16");
@@ -369,12 +372,12 @@ static int vimc_probe(struct platform_device *pdev)
 	snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info),
 		 "platform:%s", VIMC_PDEV_NAME);
 	vimc->mdev.dev = &pdev->dev;
+	vimc->mdev.ops = &vimc_mdev_ops;
 	media_device_init(&vimc->mdev);
 
 	ret = vimc_register_devices(vimc);
 	if (ret) {
-		media_device_cleanup(&vimc->mdev);
-		kfree(vimc);
+		media_device_put(&vimc->mdev);
 		return ret;
 	}
 	/*
@@ -382,7 +385,6 @@ static int vimc_probe(struct platform_device *pdev)
 	 * if the registration fails, we release directly from probe
 	 */
 
-	vimc->v4l2_dev.release = vimc_v4l2_dev_release;
 	platform_set_drvdata(pdev, vimc);
 	return 0;
 }
@@ -397,6 +399,7 @@ static void vimc_remove(struct platform_device *pdev)
 	media_device_unregister(&vimc->mdev);
 	v4l2_device_unregister(&vimc->v4l2_dev);
 	v4l2_device_put(&vimc->v4l2_dev);
+	media_device_put(&vimc->mdev);
 }
 
 static void vimc_dev_release(struct device *dev)
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 24/29] media: Documentation: Document how Media device resources are released
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (22 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 23/29] media: vimc: Release resources on media device release Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2024-02-05 15:04   ` Hans Verkuil
  2024-02-07 14:43   ` Laurent Pinchart
  2023-12-20 10:37 ` [PATCH v2 25/29] media: mc: Add per-file-handle data support Sakari Ailus
                   ` (6 subsequent siblings)
  30 siblings, 2 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

Document that after unregistering, Media device memory resources are
released by the release() callback rather than by calling
media_device_cleanup().

Also add that driver memory resources should be bound to the Media device,
not V4L2 device.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 Documentation/driver-api/media/mc-core.rst | 18 ++++++++++++++++--
 include/media/media-device.h               |  6 ++++--
 2 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/Documentation/driver-api/media/mc-core.rst b/Documentation/driver-api/media/mc-core.rst
index 2456950ce8ff..346f67760671 100644
--- a/Documentation/driver-api/media/mc-core.rst
+++ b/Documentation/driver-api/media/mc-core.rst
@@ -46,13 +46,27 @@ Drivers initialise media device instances by calling
 :c:func:`media_device_init()`. After initialising a media device instance, it is
 registered by calling :c:func:`__media_device_register()` via the macro
 ``media_device_register()`` and unregistered by calling
-:c:func:`media_device_unregister()`. An initialised media device must be
-eventually cleaned up by calling :c:func:`media_device_cleanup()`.
+:c:func:`media_device_unregister()`. The resources of an unregistered media
+device will be released by the ``release()`` callback of :c:type:`media_device`
+ops, which will be called when the last user of the media device has released it
+calling :c:func:`media_device_put()`.
+
+The ``release()`` callback is the way all the resources of the media device are
+released once :c:func:`media_device_init()` has been called. This is also
+relevant during device driver's probe function as the ``release()`` callback
+will also have to be able to safely release the resources related to a partially
+initialised media device.
 
 Note that it is not allowed to unregister a media device instance that was not
 previously registered, or clean up a media device instance that was not
 previously initialised.
 
+Media device and driver's per-device context
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Drivers should use the struct media_device_ops ``release()`` callback to release
+their own resources and not e.g. that of the struct v4l2_device.
+
 Entities
 ^^^^^^^^
 
diff --git a/include/media/media-device.h b/include/media/media-device.h
index c6816be0eee8..98e1892f1b51 100644
--- a/include/media/media-device.h
+++ b/include/media/media-device.h
@@ -250,8 +250,10 @@ void media_device_init(struct media_device *mdev);
  *
  * @mdev:	pointer to struct &media_device
  *
- * This function that will destroy the graph_mutex that is
- * initialized in media_device_init().
+ * This function that will destroy the graph_mutex that is initialized in
+ * media_device_init(). Note that *only* drivers that do not manage releasing
+ * the memory of th media device itself call this function. This function is
+ * thus effectively DEPRECATED.
  */
 void media_device_cleanup(struct media_device *mdev);
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 25/29] media: mc: Add per-file-handle data support
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (23 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 24/29] media: Documentation: Document how Media device resources are released Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2024-02-05 15:08   ` Hans Verkuil
  2023-12-20 10:37 ` [PATCH v2 26/29] media: mc: Maintain a list of open file handles in a media device Sakari Ailus
                   ` (5 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

The media devnode core associates devnodes with files by storing the
devnode pointer in the file structure private_data field. In order to
allow tracking of per-file-handle data introduce a new media devnode
file handle structure that stores the devnode pointer, and store a
pointer to that structure in the file private_data field.

Users of the media devnode code (the only existing user being
media_device) are responsible for managing their own subclass of the
media_devnode_fh structure.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

Prepare struct media_device_fh to be used for maintaining file handle list
to avoid shuffling things here and there right after.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/mc/mc-device.c  | 14 +++++++++++++-
 drivers/media/mc/mc-devnode.c | 20 +++++++++-----------
 include/media/media-device.h  |  7 +++++++
 include/media/media-devnode.h | 18 +++++++++++++++++-
 include/media/media-fh.h      | 32 ++++++++++++++++++++++++++++++++
 5 files changed, 78 insertions(+), 13 deletions(-)
 create mode 100644 include/media/media-fh.h

diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index 10426c2796b6..67a39cb63f89 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -22,6 +22,7 @@
 #include <media/media-device.h>
 #include <media/media-devnode.h>
 #include <media/media-entity.h>
+#include <media/media-fh.h>
 #include <media/media-request.h>
 
 #ifdef CONFIG_MEDIA_CONTROLLER
@@ -35,7 +36,6 @@
 #define MEDIA_ENT_SUBTYPE_MASK			0x0000ffff
 #define MEDIA_ENT_T_DEVNODE_UNKNOWN		(MEDIA_ENT_F_OLD_BASE | \
 						 MEDIA_ENT_SUBTYPE_MASK)
-
 /* -----------------------------------------------------------------------------
  * Userspace API
  */
@@ -47,11 +47,23 @@ static inline void __user *media_get_uptr(__u64 arg)
 
 static int media_device_open(struct file *filp)
 {
+	struct media_device_fh *fh;
+
+	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+	if (!fh)
+		return -ENOMEM;
+
+	filp->private_data = &fh->fh;
+
 	return 0;
 }
 
 static int media_device_close(struct file *filp)
 {
+	struct media_device_fh *fh = media_device_fh(filp);
+
+	kfree(fh);
+
 	return 0;
 }
 
diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 4ea05e42dafb..04d376015526 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -150,6 +150,7 @@ static long media_compat_ioctl(struct file *filp, unsigned int cmd,
 static int media_open(struct inode *inode, struct file *filp)
 {
 	struct media_devnode *devnode;
+	struct media_devnode_fh *fh;
 	int ret;
 
 	/* Check if the media device is available. This needs to be done with
@@ -170,17 +171,15 @@ static int media_open(struct inode *inode, struct file *filp)
 	get_device(&devnode->dev);
 	mutex_unlock(&media_devnode_lock);
 
-	filp->private_data = devnode;
-
-	if (devnode->fops->open) {
-		ret = devnode->fops->open(filp);
-		if (ret) {
-			put_device(&devnode->dev);
-			filp->private_data = NULL;
-			return ret;
-		}
+	ret = devnode->fops->open(filp);
+	if (ret) {
+		put_device(&devnode->dev);
+		return ret;
 	}
 
+	fh = filp->private_data;
+	fh->devnode = devnode;
+
 	return 0;
 }
 
@@ -189,8 +188,7 @@ static int media_release(struct inode *inode, struct file *filp)
 {
 	struct media_devnode *devnode = media_devnode_data(filp);
 
-	if (devnode->fops->release)
-		devnode->fops->release(filp);
+	devnode->fops->release(filp);
 
 	filp->private_data = NULL;
 
diff --git a/include/media/media-device.h b/include/media/media-device.h
index 98e1892f1b51..83b8ea44463d 100644
--- a/include/media/media-device.h
+++ b/include/media/media-device.h
@@ -110,6 +110,10 @@ struct media_device_ops {
  *		     other operations that stop or start streaming.
  * @request_id: Used to generate unique request IDs
  *
+ * @fh_list:	List of file handles in the media device
+ *		(struct media_device_fh.mdev_list).
+ * @fh_list_lock: Serialise access to fh_list list.
+ *
  * This structure represents an abstract high-level media device. It allows easy
  * access to entities and provides basic media device-level support. The
  * structure can be allocated directly or embedded in a larger structure.
@@ -182,6 +186,9 @@ struct media_device {
 
 	struct mutex req_queue_mutex;
 	atomic_t request_id;
+
+	struct list_head fh_list;
+	spinlock_t fh_list_lock;
 };
 
 /* We don't need to include usb.h here */
diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
index d050f54f252a..b0efdde4ffd8 100644
--- a/include/media/media-devnode.h
+++ b/include/media/media-devnode.h
@@ -53,6 +53,20 @@ struct media_file_operations {
 	int (*release) (struct file *);
 };
 
+/**
+ * struct media_devnode_fh - Media device node file handle
+ * @devnode:	pointer to the media device node
+ *
+ * This structure serves as a base for per-file-handle data storage. Media
+ * device node users embed media_devnode_fh in their custom file handle data
+ * structures and store the media_devnode_fh in the file private_data in order
+ * to let the media device node core locate the media_devnode corresponding to a
+ * file handle.
+ */
+struct media_devnode_fh {
+	struct media_devnode *devnode;
+};
+
 /**
  * struct media_devnode - Media device node
  * @media_dev:	pointer to struct &media_device
@@ -137,7 +151,9 @@ void media_devnode_unregister(struct media_devnode *devnode);
  */
 static inline struct media_devnode *media_devnode_data(struct file *filp)
 {
-	return filp->private_data;
+	struct media_devnode_fh *fh = filp->private_data;
+
+	return fh->devnode;
 }
 
 /**
diff --git a/include/media/media-fh.h b/include/media/media-fh.h
new file mode 100644
index 000000000000..6f00744b81d6
--- /dev/null
+++ b/include/media/media-fh.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Media device file handle
+ *
+ * Copyright (C) 2019--2023 Intel Corporation
+ */
+
+#ifndef MEDIA_FH_H
+#define MEDIA_FH_H
+
+#include <linux/list.h>
+#include <linux/file.h>
+
+#include <media/media-devnode.h>
+
+/**
+ * struct media_device_fh - File handle specific information on MC
+ *
+ * @fh: The media device file handle
+ * @mdev_list: This file handle in media device's list of file handles
+ */
+struct media_device_fh {
+	struct media_devnode_fh fh;
+	struct list_head mdev_list;
+};
+
+static inline struct media_device_fh *media_device_fh(struct file *filp)
+{
+	return container_of(filp->private_data, struct media_device_fh, fh);
+}
+
+#endif /* MEDIA_FH_H */
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 26/29] media: mc: Maintain a list of open file handles in a media device
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (24 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 25/29] media: mc: Add per-file-handle data support Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2024-02-05 15:11   ` Hans Verkuil
  2023-12-20 10:37 ` [PATCH v2 27/29] media: mc: Implement best effort media device removal safety sans refcount Sakari Ailus
                   ` (4 subsequent siblings)
  30 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

The list of file handles is needed to deliver media events as well as for
other purposes in the future.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/mc/mc-device.c  | 23 ++++++++++++++++++++++-
 drivers/media/mc/mc-devnode.c |  2 +-
 include/media/media-devnode.h |  4 +++-
 3 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index 67a39cb63f89..9cc055deeec9 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -45,9 +45,11 @@ static inline void __user *media_get_uptr(__u64 arg)
 	return (void __user *)(uintptr_t)arg;
 }
 
-static int media_device_open(struct file *filp)
+static int media_device_open(struct media_devnode *devnode, struct file *filp)
 {
+	struct media_device *mdev = to_media_device(devnode);
 	struct media_device_fh *fh;
+	unsigned long flags;
 
 	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
 	if (!fh)
@@ -55,12 +57,23 @@ static int media_device_open(struct file *filp)
 
 	filp->private_data = &fh->fh;
 
+	spin_lock_irqsave(&mdev->fh_list_lock, flags);
+	list_add(&fh->mdev_list, &mdev->fh_list);
+	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
+
 	return 0;
 }
 
 static int media_device_close(struct file *filp)
 {
+	struct media_devnode *devnode = media_devnode_data(filp);
+	struct media_device *mdev = to_media_device(devnode);
 	struct media_device_fh *fh = media_device_fh(filp);
+	unsigned long flags;
+
+	spin_lock_irqsave(&mdev->fh_list_lock, flags);
+	list_del(&fh->mdev_list);
+	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
 
 	kfree(fh);
 
@@ -769,11 +782,13 @@ void media_device_init(struct media_device *mdev)
 	INIT_LIST_HEAD(&mdev->pads);
 	INIT_LIST_HEAD(&mdev->links);
 	INIT_LIST_HEAD(&mdev->entity_notify);
+	INIT_LIST_HEAD(&mdev->fh_list);
 
 	mutex_init(&mdev->req_queue_mutex);
 	mutex_init(&mdev->graph_mutex);
 	ida_init(&mdev->entity_internal_idx);
 	atomic_set(&mdev->request_id, 0);
+	spin_lock_init(&mdev->fh_list_lock);
 
 	mdev->devnode.release = media_device_release;
 	media_devnode_init(&mdev->devnode);
@@ -824,6 +839,8 @@ EXPORT_SYMBOL_GPL(__media_device_register);
 
 void media_device_unregister(struct media_device *mdev)
 {
+	unsigned long flags;
+
 	if (mdev == NULL)
 		return;
 
@@ -834,6 +851,10 @@ void media_device_unregister(struct media_device *mdev)
 	}
 	mutex_unlock(&mdev->graph_mutex);
 
+	spin_lock_irqsave(&mdev->fh_list_lock, flags);
+	list_del_init(&mdev->fh_list);
+	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
+
 	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
 	dev_dbg(mdev->dev, "Media device unregistering\n");
 	media_devnode_unregister(&mdev->devnode);
diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 04d376015526..0b5c24828e24 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -171,7 +171,7 @@ static int media_open(struct inode *inode, struct file *filp)
 	get_device(&devnode->dev);
 	mutex_unlock(&media_devnode_lock);
 
-	ret = devnode->fops->open(filp);
+	ret = devnode->fops->open(devnode, filp);
 	if (ret) {
 		put_device(&devnode->dev);
 		return ret;
diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
index b0efdde4ffd8..840f7ae852d3 100644
--- a/include/media/media-devnode.h
+++ b/include/media/media-devnode.h
@@ -21,6 +21,8 @@
 #include <linux/device.h>
 #include <linux/cdev.h>
 
+struct media_devnode;
+
 /*
  * Flag to mark the media_devnode struct as registered. Drivers must not touch
  * this flag directly, it will be set and cleared by media_devnode_register and
@@ -49,7 +51,7 @@ struct media_file_operations {
 	__poll_t (*poll) (struct file *, struct poll_table_struct *);
 	long (*ioctl) (struct file *, unsigned int, unsigned long);
 	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
-	int (*open) (struct file *);
+	int (*open) (struct media_devnode *, struct file *);
 	int (*release) (struct file *);
 };
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 27/29] media: mc: Implement best effort media device removal safety sans refcount
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (25 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 26/29] media: mc: Maintain a list of open file handles in a media device Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2023-12-20 10:37 ` [PATCH v2 28/29] media: mc: Warn about drivers not releasing media device safely Sakari Ailus
                   ` (3 subsequent siblings)
  30 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

Add a new helper data structures media_devnode_compat_ref and
media_devnode_cdev. The latter is used to prevent user space from calling
IOCTLs or other system calls to the media device that has been already
unregistered and the former to help with obtaining the container struct
(either media_devnode_compat_ref or media_devnode) in media_open().

The media device's memory may of course still be released during the call
but there is only so much that can be done to this without the driver
managing the lifetime of the resources it needs somehow.

This patch should be reverted once all drivers have been converted to manage
their resources' lifetime.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/mc/mc-device.c       |  55 ++++++++++----
 drivers/media/mc/mc-devnode.c      | 117 ++++++++++++++++++++++-------
 drivers/media/v4l2-core/v4l2-dev.c |  28 +++++--
 include/media/media-devnode.h      |  64 +++++++++++++++-
 4 files changed, 210 insertions(+), 54 deletions(-)

diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index 9cc055deeec9..97d63146b344 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -55,6 +55,8 @@ static int media_device_open(struct media_devnode *devnode, struct file *filp)
 	if (!fh)
 		return -ENOMEM;
 
+	fh->fh.ref = devnode->ref;
+
 	filp->private_data = &fh->fh;
 
 	spin_lock_irqsave(&mdev->fh_list_lock, flags);
@@ -66,14 +68,19 @@ static int media_device_open(struct media_devnode *devnode, struct file *filp)
 
 static int media_device_close(struct file *filp)
 {
-	struct media_devnode *devnode = media_devnode_data(filp);
-	struct media_device *mdev = to_media_device(devnode);
-	struct media_device_fh *fh = media_device_fh(filp);
-	unsigned long flags;
+	struct media_device_fh *fh;
 
-	spin_lock_irqsave(&mdev->fh_list_lock, flags);
-	list_del(&fh->mdev_list);
-	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
+	fh = media_device_fh(filp);
+
+	if (!fh->fh.ref || atomic_read(&fh->fh.ref->registered)) {
+		struct media_devnode *devnode = media_devnode_data(filp);
+		struct media_device *mdev = to_media_device(devnode);
+		unsigned long flags;
+
+		spin_lock_irqsave(&mdev->fh_list_lock, flags);
+		list_del(&fh->mdev_list);
+		spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
+	}
 
 	kfree(fh);
 
@@ -812,28 +819,45 @@ EXPORT_SYMBOL_GPL(media_device_cleanup);
 int __must_check __media_device_register(struct media_device *mdev,
 					 struct module *owner)
 {
+	struct media_devnode_compat_ref *ref = NULL;
 	int ret;
 
+	if (!mdev->ops || !mdev->ops->release) {
+		ref = kzalloc(sizeof(*mdev->devnode.ref), GFP_KERNEL);
+		if (!ref)
+			return -ENOMEM;
+	}
+
 	/* Register the device node. */
 	mdev->devnode.fops = &media_device_fops;
 	mdev->devnode.parent = mdev->dev;
+	mdev->devnode.ref = ref;
 
 	/* Set version 0 to indicate user-space that the graph is static */
 	mdev->topology_version = 0;
 
 	ret = media_devnode_register(&mdev->devnode, owner);
 	if (ret < 0)
-		return ret;
+		goto out_put_ref;
+
+	ret = device_create_file(media_devnode_dev(&mdev->devnode),
+				 &dev_attr_model);
+	if (ret < 0)
+		goto out_devnode_unregister;
 
-	ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
-	if (ret < 0) {
-		media_devnode_unregister(&mdev->devnode);
-		return ret;
-	}
 
 	dev_dbg(mdev->dev, "Media device registered\n");
 
 	return 0;
+
+out_devnode_unregister:
+	media_devnode_unregister(&mdev->devnode);
+
+out_put_ref:
+	if (ref)
+		put_device(&ref->dev);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(__media_device_register);
 
@@ -855,9 +879,12 @@ void media_device_unregister(struct media_device *mdev)
 	list_del_init(&mdev->fh_list);
 	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
 
-	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
+	device_remove_file(media_devnode_dev(&mdev->devnode), &dev_attr_model);
 	dev_dbg(mdev->dev, "Media device unregistering\n");
 	media_devnode_unregister(&mdev->devnode);
+
+	if (mdev->devnode.ref)
+		put_device(&mdev->devnode.ref->dev);
 }
 EXPORT_SYMBOL_GPL(media_device_unregister);
 
diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index 0b5c24828e24..d64bb501a3ee 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -71,19 +71,45 @@ static void media_devnode_release(struct device *cd)
 		devnode->release(devnode);
 }
 
+static void media_devnode_ref_release(struct device *cd)
+{
+	struct media_devnode_compat_ref *ref =
+		container_of_const(cd, struct media_devnode_compat_ref, dev);
+
+	media_devnode_free_minor(ref->minor);
+
+	kfree(ref);
+}
+
+struct media_devnode *to_media_devnode(struct device *dev)
+{
+	if (dev->release == media_devnode_release)
+		return container_of(dev, struct media_devnode, dev);
+
+	return container_of(dev, struct media_devnode_compat_ref, dev)->devnode;
+}
+
 static struct bus_type media_bus_type = {
 	.name = MEDIA_NAME,
 };
 
+static bool media_devnode_is_registered_compat(struct media_devnode_fh *fh)
+{
+	if (fh->ref)
+		return atomic_read(&fh->ref->registered);
+
+	return media_devnode_is_registered(fh->devnode);
+}
+
 static ssize_t media_read(struct file *filp, char __user *buf,
 		size_t sz, loff_t *off)
 {
 	struct media_devnode *devnode = media_devnode_data(filp);
 
+	if (!media_devnode_is_registered_compat(filp->private_data))
+		return -EIO;
 	if (!devnode->fops->read)
 		return -EINVAL;
-	if (!media_devnode_is_registered(devnode))
-		return -EIO;
 	return devnode->fops->read(filp, buf, sz, off);
 }
 
@@ -92,10 +118,10 @@ static ssize_t media_write(struct file *filp, const char __user *buf,
 {
 	struct media_devnode *devnode = media_devnode_data(filp);
 
+	if (!media_devnode_is_registered_compat(filp->private_data))
+		return -EIO;
 	if (!devnode->fops->write)
 		return -EINVAL;
-	if (!media_devnode_is_registered(devnode))
-		return -EIO;
 	return devnode->fops->write(filp, buf, sz, off);
 }
 
@@ -104,7 +130,7 @@ static __poll_t media_poll(struct file *filp,
 {
 	struct media_devnode *devnode = media_devnode_data(filp);
 
-	if (!media_devnode_is_registered(devnode))
+	if (!media_devnode_is_registered_compat(filp->private_data))
 		return EPOLLERR | EPOLLHUP;
 	if (!devnode->fops->poll)
 		return DEFAULT_POLLMASK;
@@ -116,14 +142,9 @@ __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg,
 	      long (*ioctl_func)(struct file *filp, unsigned int cmd,
 				 unsigned long arg))
 {
-	struct media_devnode *devnode = media_devnode_data(filp);
-
 	if (!ioctl_func)
 		return -ENOTTY;
 
-	if (!media_devnode_is_registered(devnode))
-		return -EIO;
-
 	return ioctl_func(filp, cmd, arg);
 }
 
@@ -131,6 +152,9 @@ static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct media_devnode *devnode = media_devnode_data(filp);
 
+	if (!media_devnode_is_registered_compat(filp->private_data))
+		return -EIO;
+
 	return __media_ioctl(filp, cmd, arg, devnode->fops->ioctl);
 }
 
@@ -141,6 +165,9 @@ static long media_compat_ioctl(struct file *filp, unsigned int cmd,
 {
 	struct media_devnode *devnode = media_devnode_data(filp);
 
+	if (!media_devnode_is_registered_compat(filp->private_data))
+		return -EIO;
+
 	return __media_ioctl(filp, cmd, arg, devnode->fops->compat_ioctl);
 }
 
@@ -149,6 +176,7 @@ static long media_compat_ioctl(struct file *filp, unsigned int cmd,
 /* Override for the open function */
 static int media_open(struct inode *inode, struct file *filp)
 {
+	struct media_devnode_cdev *mcdev;
 	struct media_devnode *devnode;
 	struct media_devnode_fh *fh;
 	int ret;
@@ -160,7 +188,12 @@ static int media_open(struct inode *inode, struct file *filp)
 	 * a crash.
 	 */
 	mutex_lock(&media_devnode_lock);
-	devnode = container_of(inode->i_cdev, struct media_devnode, cdev);
+	mcdev = container_of(inode->i_cdev, struct media_devnode_cdev, cdev);
+	if (mcdev->is_compat_ref)
+		devnode = container_of(mcdev, struct media_devnode_compat_ref,
+				       mcdev)->devnode;
+	else
+		devnode = container_of(mcdev, struct media_devnode, mcdev);
 	/* return ENXIO if the media device has been removed
 	   already or if it is not registered anymore. */
 	if (!media_devnode_is_registered(devnode)) {
@@ -168,12 +201,12 @@ static int media_open(struct inode *inode, struct file *filp)
 		return -ENXIO;
 	}
 	/* and increase the device refcount */
-	get_device(&devnode->dev);
+	get_device(media_devnode_dev(devnode));
 	mutex_unlock(&media_devnode_lock);
 
 	ret = devnode->fops->open(devnode, filp);
 	if (ret) {
-		put_device(&devnode->dev);
+		put_device(media_devnode_dev(devnode));
 		return ret;
 	}
 
@@ -186,15 +219,21 @@ static int media_open(struct inode *inode, struct file *filp)
 /* Override for the release function */
 static int media_release(struct inode *inode, struct file *filp)
 {
-	struct media_devnode *devnode = media_devnode_data(filp);
-
-	devnode->fops->release(filp);
+	struct media_devnode_fh *fh = filp->private_data;
+	struct device *dev;
+
+	if (!fh->ref) {
+		dev = &fh->devnode->dev;
+		fh->devnode->fops->release(filp);
+	} else {
+		dev = &fh->ref->dev;
+		fh->ref->release(filp);
+	}
 
 	filp->private_data = NULL;
 
-	/* decrease the refcount unconditionally since the release()
-	   return value is ignored. */
-	put_device(&devnode->dev);
+	put_device(dev);
+
 	return 0;
 }
 
@@ -222,6 +261,9 @@ void media_devnode_init(struct media_devnode *devnode)
 int __must_check media_devnode_register(struct media_devnode *devnode,
 					struct module *owner)
 {
+	struct media_devnode_compat_ref *ref = devnode->ref;
+	struct cdev *cdev;
+	struct device *dev;
 	int minor;
 	int ret;
 
@@ -243,18 +285,31 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
 	devnode->minor = minor;
 
 	/* Part 2: Initialize the media and character devices */
-	cdev_init(&devnode->cdev, &media_devnode_fops);
-	devnode->cdev.owner = owner;
-	kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor);
-
-	devnode->dev.bus = &media_bus_type;
-	devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
+	cdev = ref ? &ref->mcdev.cdev : &devnode->mcdev.cdev;
+	cdev_init(cdev, &media_devnode_fops);
+	cdev->owner = owner;
+	kobject_set_name(&cdev->kobj, "media%d", devnode->minor);
+
+	if (!ref) {
+		dev = &devnode->dev;
+	} else {
+		ref->mcdev.is_compat_ref = true;
+		device_initialize(&ref->dev);
+		atomic_set(&ref->registered, 1);
+		ref->devnode = devnode;
+		ref->minor = devnode->minor;
+		ref->release = devnode->fops->release;
+		dev = &ref->dev;
+		dev->release = media_devnode_ref_release;
+	}
+	dev->bus = &media_bus_type;
+	dev->devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
 	if (devnode->parent)
-		devnode->dev.parent = devnode->parent;
-	dev_set_name(&devnode->dev, "media%d", devnode->minor);
+		dev->parent = devnode->parent;
+	dev_set_name(dev, "media%d", devnode->minor);
 
 	/* Part 3: Add the media and character devices */
-	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
+	ret = cdev_device_add(cdev, dev);
 	if (ret < 0) {
 		pr_err("%s: cdev_device_add failed\n", __func__);
 		goto cdev_add_error;
@@ -279,11 +334,15 @@ void media_devnode_unregister(struct media_devnode *devnode)
 	if (!media_devnode_is_registered(devnode))
 		return;
 
+	if (devnode->ref)
+		atomic_set(&devnode->ref->registered, 0);
+
 	mutex_lock(&media_devnode_lock);
 	clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
 	mutex_unlock(&media_devnode_lock);
 
-	cdev_device_del(&devnode->cdev, &devnode->dev);
+	cdev_device_del(devnode->ref ? &devnode->ref->mcdev.cdev :
+			&devnode->mcdev.cdev, media_devnode_dev(devnode));
 }
 
 /*
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index c1e4995eaf5c..e18e7199ed83 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -179,7 +179,7 @@ static void v4l2_device_release(struct device *cd)
 	bool v4l2_dev_has_release = v4l2_dev->release;
 #ifdef CONFIG_MEDIA_CONTROLLER
 	struct media_device *mdev = v4l2_dev->mdev;
-	bool mdev_has_release = mdev && mdev->ops && mdev->ops->release;
+	bool mdev_has_ref = mdev && mdev->devnode.ref;
 #endif
 
 	mutex_lock(&videodev_lock);
@@ -212,12 +212,24 @@ static void v4l2_device_release(struct device *cd)
 	}
 #endif
 
-	/* Release video_device and perform other
-	   cleanups as needed. */
+	/*
+	 * Put struct media_devnode_compat_ref here as indicated by
+	 * mdev_has_ref. mdev may be released by vdev->release() below.
+	 */
+#ifdef CONFIG_MEDIA_CONTROLLER
+	if (mdev && mdev_has_ref)
+		media_device_put(mdev);
+#endif
+
+	/* Release video_device and perform other cleanups as needed. */
 	vdev->release(vdev);
 
 #ifdef CONFIG_MEDIA_CONTROLLER
-	if (mdev)
+	/*
+	 * Put a reference to struct media_device acquired in
+	 * video_register_media_controller().
+	 */
+	if (mdev && !mdev_has_ref)
 		media_device_put(mdev);
 
 	/*
@@ -225,16 +237,18 @@ static void v4l2_device_release(struct device *cd)
 	 * embedded in the same driver's context struct so having a release
 	 * callback in both is a bug.
 	 */
-	WARN_ON(v4l2_dev_has_release && mdev_has_release);
+	WARN_ON(v4l2_dev_has_release && !mdev_has_ref);
 #endif
 
 	/*
 	 * Decrease v4l2_device refcount, but only if the media device doesn't
-	 * have a release callback.
+	 * have a release callback. Otherwise one could expect accessing
+	 * released memory --- driver's context struct refcounted already via
+	 * struct media_device.
 	 */
 	if (v4l2_dev_has_release
 #ifdef CONFIG_MEDIA_CONTROLLER
-	    && !mdev_has_release
+	    && mdev_has_ref
 #endif
 	    )
 		v4l2_device_put(v4l2_dev);
diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
index 840f7ae852d3..85d54e2c9a97 100644
--- a/include/media/media-devnode.h
+++ b/include/media/media-devnode.h
@@ -41,8 +41,6 @@ struct media_devnode;
  * @compat_ioctl: pointer to the function that will handle 32 bits userspace
  *	calls to the ioctl() syscall on a Kernel compiled with 64 bits.
  * @open: pointer to the function that implements open() syscall
- * @release: pointer to the function that will release the resources allocated
- *	by the @open function.
  */
 struct media_file_operations {
 	struct module *owner;
@@ -55,9 +53,56 @@ struct media_file_operations {
 	int (*release) (struct file *);
 };
 
+/**
+ * struct media_devnode_cdev - Workaround for drivers not managing media device
+ *			       lifetime - character device
+ *
+ * Store the characeter device and whether this is a compatibility reference or
+ * not. struct media_devnode_cdev is embedded in either struct
+ * media_devnode_compat_ref or struct media_devnode.
+ *
+ * @cdev: the chracter device
+ * @is_compat_ref: Is this a compatibility reference or not?
+ */
+struct media_devnode_cdev {
+	struct cdev cdev;
+	bool is_compat_ref;
+};
+
+/**
+ * struct media_devnode_compat_ref - Workaround for drivers not managing media
+ *				     device lifetime - reference
+ *
+ * The purpose if this struct is to support drivers that do not manage the
+ * lifetime of their respective media devices to avoid the worst effects of
+ * this, namely an IOCTL call on an open file handle to a device that has been
+ * unbound causing a kernel oops systematically. This is not a fix. The proper,
+ * reliable way to handle this is to manage the resources used by the
+ * driver. This struct and its use can be removed once all drivers have been
+ * converted.
+ *
+ * @dev: struct device that remains in place as long as any reference remains
+ * @cdev: the related character device
+ * @devnode: a pointer back to the devnode
+ * @release:	pointer to the function that will release the resources
+ *		allocated by the @open function.
+ * @registered: is this device registered?
+ * @minor: the minor number of the media devnode
+ */
+struct media_devnode_compat_ref {
+	struct device dev;
+	struct media_devnode_cdev mcdev;
+	struct media_devnode *devnode;
+	int (*release)(struct file *);
+	atomic_t registered;
+	unsigned int minor;
+};
+
 /**
  * struct media_devnode_fh - Media device node file handle
  * @devnode:	pointer to the media device node
+ * @ref:	media device compat ref, if the driver does not manage media
+ *		device lifetime
  *
  * This structure serves as a base for per-file-handle data storage. Media
  * device node users embed media_devnode_fh in their custom file handle data
@@ -67,6 +112,7 @@ struct media_file_operations {
  */
 struct media_devnode_fh {
 	struct media_devnode *devnode;
+	struct media_devnode_compat_ref *ref;
 };
 
 /**
@@ -80,6 +126,8 @@ struct media_devnode_fh {
  * @flags:	flags, combination of the ``MEDIA_FLAG_*`` constants
  * @release:	release callback called at the end of ``media_devnode_release()``
  *		routine at media-device.c.
+ * @ref:	reference for providing best effort memory safety in device
+ *		removal
  *
  * This structure represents a media-related device node.
  *
@@ -92,7 +140,7 @@ struct media_devnode {
 
 	/* sysfs */
 	struct device dev;		/* media device */
-	struct cdev cdev;		/* character device */
+	struct media_devnode_cdev mcdev; /* character device + compat status */
 	struct device *parent;		/* device parent */
 
 	/* device info */
@@ -101,10 +149,18 @@ struct media_devnode {
 
 	/* callbacks */
 	void (*release)(struct media_devnode *devnode);
+
+	/* compat reference */
+	struct media_devnode_compat_ref *ref;
 };
 
+static inline struct device *media_devnode_dev(struct media_devnode *devnode)
+{
+	return devnode->ref ? &devnode->ref->dev : &devnode->dev;
+}
+
 /* dev to media_devnode */
-#define to_media_devnode(cd) container_of(cd, struct media_devnode, dev)
+struct media_devnode *to_media_devnode(struct device *dev);
 
 /**
  * media_devnode_init - initialise a media devnode
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 28/29] media: mc: Warn about drivers not releasing media device safely
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (26 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 27/29] media: mc: Implement best effort media device removal safety sans refcount Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2023-12-20 10:37 ` [PATCH v2 29/29] media: Documentation: Document media device memory safety helper Sakari Ailus
                   ` (2 subsequent siblings)
  30 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

The media device and associated resources may be released only when its
memory is no longer used. Warn about drivers not doing this, but instead
releasing the resources at driver unbind time.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/mc/mc-device.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
index 97d63146b344..a1ca2a56269d 100644
--- a/drivers/media/mc/mc-device.c
+++ b/drivers/media/mc/mc-device.c
@@ -826,6 +826,9 @@ int __must_check __media_device_register(struct media_device *mdev,
 		ref = kzalloc(sizeof(*mdev->devnode.ref), GFP_KERNEL);
 		if (!ref)
 			return -ENOMEM;
+
+		dev_warn(mdev->dev,
+			 "Set mdev release op to safely release resources!\n");
 	}
 
 	/* Register the device node. */
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* [PATCH v2 29/29] media: Documentation: Document media device memory safety helper
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (27 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 28/29] media: mc: Warn about drivers not releasing media device safely Sakari Ailus
@ 2023-12-20 10:37 ` Sakari Ailus
  2023-12-20 10:52 ` [PATCH v2 00/29] Media device lifetime management Laurent Pinchart
  2024-02-07 10:55 ` Laurent Pinchart
  30 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 10:37 UTC (permalink / raw)
  To: linux-media; +Cc: laurent.pinchart, Hans Verkuil

Document how the best effort memory safety helper for accessing media
device works, and that drivers should be converted to refcount the media
device.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/mc/mc-devnode.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
index d64bb501a3ee..ef8e57a307a6 100644
--- a/drivers/media/mc/mc-devnode.c
+++ b/drivers/media/mc/mc-devnode.c
@@ -258,6 +258,32 @@ void media_devnode_init(struct media_devnode *devnode)
 	devnode->minor = -1;
 }
 
+/*
+ * Best effort media device lifetime management for old drivers
+ *
+ * Drivers that do not manage the lifetime of the media device are provided with
+ * a best effort lifetime management support. This means that as the driver does
+ * not release the media device once all users are gone but when the device is
+ * unbound, there are bound to be (brief) moments when released memory may get
+ * accessed. All drivers should be converted to release their memory at a safe
+ * time, i.e. provide a release callback in struct media_file_operations to do
+ * so. This is especially important for drivers for devices that are
+ * unpluggable, e.g. USB devices.
+ *
+ * A second struct device is used to manage the lifetime of a helper object,
+ * struct media_devnode_compat_ref. For a media device, one is initialised in
+ * media_devnode_register and put in media_devnode_unregister. This object is
+ * also used as the device of the media character device so file handles to the
+ * media device have a reference to this object. When the media device is
+ * released, any file handle retains a reference to this helper that also
+ * contains the media device's registration status. If a media device is
+ * released and a user space process attempts to access the file handle, an
+ * error is returned.
+ *
+ * The struct device in struct media_devnode is put at media_device_cleanup and
+ * uses an empty release callback, reflecting the expectation the driver will
+ * release the memory of the media device at unbind time.
+ */
 int __must_check media_devnode_register(struct media_devnode *devnode,
 					struct module *owner)
 {
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 00/29] Media device lifetime management
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (28 preceding siblings ...)
  2023-12-20 10:37 ` [PATCH v2 29/29] media: Documentation: Document media device memory safety helper Sakari Ailus
@ 2023-12-20 10:52 ` Laurent Pinchart
  2023-12-20 11:30   ` Sakari Ailus
  2024-02-07 10:55 ` Laurent Pinchart
  30 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2023-12-20 10:52 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

On Wed, Dec 20, 2023 at 12:36:44PM +0200, Sakari Ailus wrote:
> Hi folks,
> 
> This is a refresh of my 2016 RFC patchset to start addressing object
> lifetime issues in Media controller. It further allows continuing work to
> address lifetime management of media entities.

I think you win the prize of the refresh for the oldest patch series.
Thanks for not dropping the ball. One day we'll make the media subsystem
healthy :-)

> The underlying problem is described in detail in v4 of the previous RFC:
> <URL:https://lore.kernel.org/linux-media/20161108135438.GO3217@valkosipuli.retiisi.org.uk/>.
> In brief, there is currently no connection between releasing media device
> (and related) memory and IOCTL calls, meaning that there is a time window
> during which released kernel memory can be accessed, and that access can be
> triggered from the user space. The only reason why this is not a grave
> security issue is that it is not triggerable by the user alone but requires
> unbinding a device. That is still not an excuse for not fixing it.
> 
> This set differs from the earlier RFC to address the issue in the
> following respects:
> 
> - Make changes for ipu3-cio2 driver, too.
> 
> - Continue to provide best effort attempt to keep the window between device
>   removal and user space being able to access released memory as small as
>   possible. This means the problem won't become worse for drivers for which
>   Media device lifetime management has not been implemented.
> 
> The latter is achieved by adding a new object, Media devnode compat
> reference, which is allocated, refcounted and eventually released by the
> Media controller framework itself, and where the information on registration
> and open filehandles is maintained. This is only done if the driver does not
> manage the lifetime of the media device itself, i.e. its release operation
> is NULL.

Interesting. I'll check that when reviewing the patches.

> Due to this, Media device file handles will also be introduced by this
> patchset. I thought the first user of this would be Media device events but
> it seems we already need them here.

Nice, it will become a useful feature.

> Both ipu3-cio2 and omap3isp drivers are relieved of devm_request_irq() use,
> as device_release() releases the resources before calling the driver's
> remove function.

Are you sure about that ? device_release() is the .release() function
for device_ktype, which means it's called when the last reference to a
struct device disappears. That should be way after .remove().

> While further work will be required also on these drivers
> to safely stop he hardware at unbind time, I don't see a reason not to merge
> these patches now.
> 
> Some patches are temporarily reverted in order to make reworks easier, then
> applied later on.
> 
> I've tested this on ipu3-cio2 with and without the refcounting patch (media:
> ipu3-cio2: Release the cio2 device context by media device callback),
> including failures in a few parts of the driver initialisation process in
> the MC framework.
> 
> Questions and comments are welcome.
> 
> since v1:
> 
> - Align subject prefixes with current media tree practices.
> 
> - Make release changes to the vimc driver (last patch of the set). This
>   was actually easy as vimc already centralised resource release to struct
>   v4l2_device, so it was just moved to the media device.
> 
> - Move cdev field to struct media_devnode_compat_ref and add dev field to
>   the struct, these are needed during device release. This now includes
>   also the character device which is accessed by __fput(). I've now tested
>   ipu3-cio2 and vimc with KASAN. As a by-product the kref in struct
>   media_devnode_compat_ref becomes redundant and is removed. Both devices
>   are registered in case of best effort memory safety support and used for
>   refcounting.
> 
> - Drop omap3isp driver patch moving away from devm_request_irq().
> 
> - Add a patch to warn of drivers not releasing media device safely (i.e.
>   relying on the best effort memory safety mechanism without refcounting).
> 
> - Add a patch to document how the best effort memory release safety helper
>   works.
> 
> - Add a note on releasing driver's context with the media device, not the
>   V4L2 device, in MC documentation.
> 
> - Check media device is registered before accessing its fops in
>   media_read(), media_write(), media_ioctl and media_compat_ioctl().
> 
> - Document best effort media device lifetime management (new patch).
> 
> - Use media_devnode_free_minor() in unallocating device node minor number
>   in media_devnode_register().
> 
> - Continue to rely on devm_register_irq() in ipu3-cio2 driver but register
>   the IRQ later on (compared to v1).
> 
> - Drop the patch to move away from devm_request_irq() in omap3isp.
> 
> - Fix putting references to media device and V4L2 device in 
>   v4l2_device_release().
> 
> - Add missing media_device_get() (in v1) for M2M devices in
>   video_register_media_controller().
> 
> - Unconditionally set the media devnode release function in
>   media_device_init(). There's no harm doing so and the caller of
>   media_device_init() may set the ops after calling the function.
> 
> Daniel Axtens (1):
>   media: uvcvideo: Refactor teardown of uvc on USB disconnect
> 
> Laurent Pinchart (1):
>   media: mc: Add per-file-handle data support
> 
> Logan Gunthorpe (1):
>   media: mc: utilize new cdev_device_add helper function
> 
> Sakari Ailus (26):
>   Revert "[media] media: fix media devnode ioctl/syscall and unregister
>     race"
>   Revert "media: utilize new cdev_device_add helper function"
>   Revert "[media] media: fix use-after-free in cdev_put() when app exits
>     after driver unbind"
>   Revert "media: uvcvideo: Refactor teardown of uvc on USB disconnect"
>   Revert "[media] media-device: dynamically allocate struct
>     media_devnode"
>   media: mc: Drop nop release callback
>   media: mc: Do not call cdev_device_del() if cdev_device_add() fails
>   media: mc: Delete character device early
>   media: mc: Split initialising and adding media devnode
>   media: mc: Shuffle functions around
>   media: mc: Initialise media devnode in media_device_init()
>   media: mc: Refactor media devnode minor clearing
>   media: mc: Unassign minor only if it has been assigned
>   media: mc: Refcount the media device
>   media: v4l: Acquire a reference to the media device for every video
>     device
>   media: mc: Postpone graph object removal until free
>   media: omap3isp: Release the isp device struct by media device
>     callback
>   media: ipu3-cio2: Call v4l2_device_unregister() earlier
>   media: ipu3-cio2: Request IRQ earlier
>   media: ipu3-cio2: Release the cio2 device context by media device
>     callback
>   media: vimc: Release resources on media device release
>   media: Documentation: Document how Media device resources are released
>   media: mc: Maintain a list of open file handles in a media device
>   media: mc: Implement best effort media device removal safety sans
>     refcount
>   media: mc: Warn about drivers not releasing media device safely
>   media: Documentation: Document media device memory safety helper
> 
>  Documentation/driver-api/media/mc-core.rst  |  18 +-
>  drivers/media/cec/core/cec-core.c           |   2 +-
>  drivers/media/mc/mc-device.c                | 260 ++++++++++++--------
>  drivers/media/mc/mc-devnode.c               | 230 +++++++++++------
>  drivers/media/pci/intel/ipu3/ipu3-cio2.c    |  70 ++++--
>  drivers/media/platform/ti/omap3isp/isp.c    |  24 +-
>  drivers/media/test-drivers/vimc/vimc-core.c |  15 +-
>  drivers/media/usb/au0828/au0828-core.c      |   4 +-
>  drivers/media/usb/uvc/uvc_driver.c          |   2 +-
>  drivers/media/v4l2-core/v4l2-dev.c          |  65 +++--
>  drivers/staging/media/sunxi/cedrus/cedrus.c |   2 +-
>  include/media/media-device.h                |  46 +++-
>  include/media/media-devnode.h               | 136 +++++++---
>  include/media/media-fh.h                    |  32 +++
>  14 files changed, 632 insertions(+), 274 deletions(-)
>  create mode 100644 include/media/media-fh.h

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 00/29] Media device lifetime management
  2023-12-20 10:52 ` [PATCH v2 00/29] Media device lifetime management Laurent Pinchart
@ 2023-12-20 11:30   ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2023-12-20 11:30 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil

Hi Laurent,

On Wed, Dec 20, 2023 at 12:52:32PM +0200, Laurent Pinchart wrote:
> On Wed, Dec 20, 2023 at 12:36:44PM +0200, Sakari Ailus wrote:
> > Hi folks,
> > 
> > This is a refresh of my 2016 RFC patchset to start addressing object
> > lifetime issues in Media controller. It further allows continuing work to
> > address lifetime management of media entities.
> 
> I think you win the prize of the refresh for the oldest patch series.
> Thanks for not dropping the ball. One day we'll make the media subsystem
> healthy :-)

Maybe so, indeed. The problem has always been around and I think it needs
to be addressed, although this only solves a part of it. It's been a low
priority for me and figuring out how to prevent old drivers getting worse
in this respect wasn't obvious at first, as you see from changes since v1.

> 
> > The underlying problem is described in detail in v4 of the previous RFC:
> > <URL:https://lore.kernel.org/linux-media/20161108135438.GO3217@valkosipuli.retiisi.org.uk/>.
> > In brief, there is currently no connection between releasing media device
> > (and related) memory and IOCTL calls, meaning that there is a time window
> > during which released kernel memory can be accessed, and that access can be
> > triggered from the user space. The only reason why this is not a grave
> > security issue is that it is not triggerable by the user alone but requires
> > unbinding a device. That is still not an excuse for not fixing it.
> > 
> > This set differs from the earlier RFC to address the issue in the
> > following respects:
> > 
> > - Make changes for ipu3-cio2 driver, too.
> > 
> > - Continue to provide best effort attempt to keep the window between device
> >   removal and user space being able to access released memory as small as
> >   possible. This means the problem won't become worse for drivers for which
> >   Media device lifetime management has not been implemented.
> > 
> > The latter is achieved by adding a new object, Media devnode compat
> > reference, which is allocated, refcounted and eventually released by the
> > Media controller framework itself, and where the information on registration
> > and open filehandles is maintained. This is only done if the driver does not
> > manage the lifetime of the media device itself, i.e. its release operation
> > is NULL.
> 
> Interesting. I'll check that when reviewing the patches.

Please. :-)

> 
> > Due to this, Media device file handles will also be introduced by this
> > patchset. I thought the first user of this would be Media device events but
> > it seems we already need them here.
> 
> Nice, it will become a useful feature.
> 
> > Both ipu3-cio2 and omap3isp drivers are relieved of devm_request_irq() use,
> > as device_release() releases the resources before calling the driver's
> > remove function.
> 
> Are you sure about that ? device_release() is the .release() function
> for device_ktype, which means it's called when the last reference to a
> struct device disappears. That should be way after .remove().

It's a bit grey area. If an interrupt arrives from the device in the
meantime, bad things will still happen. Of course it's not supposed to.
Either way, I've dropped these changes as requested in review comments in
v1 and as outlined below in changes since v1, but forgot to change this
text (unchanged since v1).

> 
> > While further work will be required also on these drivers
> > to safely stop he hardware at unbind time, I don't see a reason not to merge
> > these patches now.
> > 
> > Some patches are temporarily reverted in order to make reworks easier, then
> > applied later on.
> > 
> > I've tested this on ipu3-cio2 with and without the refcounting patch (media:
> > ipu3-cio2: Release the cio2 device context by media device callback),
> > including failures in a few parts of the driver initialisation process in
> > the MC framework.
> > 
> > Questions and comments are welcome.
> > 
> > since v1:
> > 
> > - Align subject prefixes with current media tree practices.
> > 
> > - Make release changes to the vimc driver (last patch of the set). This
> >   was actually easy as vimc already centralised resource release to struct
> >   v4l2_device, so it was just moved to the media device.
> > 
> > - Move cdev field to struct media_devnode_compat_ref and add dev field to
> >   the struct, these are needed during device release. This now includes
> >   also the character device which is accessed by __fput(). I've now tested
> >   ipu3-cio2 and vimc with KASAN. As a by-product the kref in struct
> >   media_devnode_compat_ref becomes redundant and is removed. Both devices
> >   are registered in case of best effort memory safety support and used for
> >   refcounting.
> > 
> > - Drop omap3isp driver patch moving away from devm_request_irq().
> > 
> > - Add a patch to warn of drivers not releasing media device safely (i.e.
> >   relying on the best effort memory safety mechanism without refcounting).
> > 
> > - Add a patch to document how the best effort memory release safety helper
> >   works.
> > 
> > - Add a note on releasing driver's context with the media device, not the
> >   V4L2 device, in MC documentation.
> > 
> > - Check media device is registered before accessing its fops in
> >   media_read(), media_write(), media_ioctl and media_compat_ioctl().
> > 
> > - Document best effort media device lifetime management (new patch).
> > 
> > - Use media_devnode_free_minor() in unallocating device node minor number
> >   in media_devnode_register().
> > 
> > - Continue to rely on devm_register_irq() in ipu3-cio2 driver but register
> >   the IRQ later on (compared to v1).
> > 
> > - Drop the patch to move away from devm_request_irq() in omap3isp.
> > 
> > - Fix putting references to media device and V4L2 device in 
> >   v4l2_device_release().
> > 
> > - Add missing media_device_get() (in v1) for M2M devices in
> >   video_register_media_controller().
> > 
> > - Unconditionally set the media devnode release function in
> >   media_device_init(). There's no harm doing so and the caller of
> >   media_device_init() may set the ops after calling the function.
> > 
> > Daniel Axtens (1):
> >   media: uvcvideo: Refactor teardown of uvc on USB disconnect
> > 
> > Laurent Pinchart (1):
> >   media: mc: Add per-file-handle data support
> > 
> > Logan Gunthorpe (1):
> >   media: mc: utilize new cdev_device_add helper function
> > 
> > Sakari Ailus (26):
> >   Revert "[media] media: fix media devnode ioctl/syscall and unregister
> >     race"
> >   Revert "media: utilize new cdev_device_add helper function"
> >   Revert "[media] media: fix use-after-free in cdev_put() when app exits
> >     after driver unbind"
> >   Revert "media: uvcvideo: Refactor teardown of uvc on USB disconnect"
> >   Revert "[media] media-device: dynamically allocate struct
> >     media_devnode"
> >   media: mc: Drop nop release callback
> >   media: mc: Do not call cdev_device_del() if cdev_device_add() fails
> >   media: mc: Delete character device early
> >   media: mc: Split initialising and adding media devnode
> >   media: mc: Shuffle functions around
> >   media: mc: Initialise media devnode in media_device_init()
> >   media: mc: Refactor media devnode minor clearing
> >   media: mc: Unassign minor only if it has been assigned
> >   media: mc: Refcount the media device
> >   media: v4l: Acquire a reference to the media device for every video
> >     device
> >   media: mc: Postpone graph object removal until free
> >   media: omap3isp: Release the isp device struct by media device
> >     callback
> >   media: ipu3-cio2: Call v4l2_device_unregister() earlier
> >   media: ipu3-cio2: Request IRQ earlier
> >   media: ipu3-cio2: Release the cio2 device context by media device
> >     callback
> >   media: vimc: Release resources on media device release
> >   media: Documentation: Document how Media device resources are released
> >   media: mc: Maintain a list of open file handles in a media device
> >   media: mc: Implement best effort media device removal safety sans
> >     refcount
> >   media: mc: Warn about drivers not releasing media device safely
> >   media: Documentation: Document media device memory safety helper
> > 
> >  Documentation/driver-api/media/mc-core.rst  |  18 +-
> >  drivers/media/cec/core/cec-core.c           |   2 +-
> >  drivers/media/mc/mc-device.c                | 260 ++++++++++++--------
> >  drivers/media/mc/mc-devnode.c               | 230 +++++++++++------
> >  drivers/media/pci/intel/ipu3/ipu3-cio2.c    |  70 ++++--
> >  drivers/media/platform/ti/omap3isp/isp.c    |  24 +-
> >  drivers/media/test-drivers/vimc/vimc-core.c |  15 +-
> >  drivers/media/usb/au0828/au0828-core.c      |   4 +-
> >  drivers/media/usb/uvc/uvc_driver.c          |   2 +-
> >  drivers/media/v4l2-core/v4l2-dev.c          |  65 +++--
> >  drivers/staging/media/sunxi/cedrus/cedrus.c |   2 +-
> >  include/media/media-device.h                |  46 +++-
> >  include/media/media-devnode.h               | 136 +++++++---
> >  include/media/media-fh.h                    |  32 +++
> >  14 files changed, 632 insertions(+), 274 deletions(-)
> >  create mode 100644 include/media/media-fh.h
> 

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 14/29] media: mc: Refactor media devnode minor clearing
  2023-12-20 10:36 ` [PATCH v2 14/29] media: mc: Refactor media devnode minor clearing Sakari Ailus
@ 2024-02-05 14:46   ` Hans Verkuil
  2024-02-07 10:53   ` Laurent Pinchart
  1 sibling, 0 replies; 92+ messages in thread
From: Hans Verkuil @ 2024-02-05 14:46 UTC (permalink / raw)
  To: Sakari Ailus, linux-media; +Cc: laurent.pinchart

On 20/12/2023 11:36, Sakari Ailus wrote:
> Refactor clearing media devnode minor bit in media devnode bitmap. Note
> that number is used instead of struct media_devnode as argument since the
> minor number will also be stored in a different structure soon.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>

Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>

> ---
>  drivers/media/mc/mc-devnode.c | 19 +++++++++++--------
>  1 file changed, 11 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> index 7b17419050fb..717408791a7c 100644
> --- a/drivers/media/mc/mc-devnode.c
> +++ b/drivers/media/mc/mc-devnode.c
> @@ -44,17 +44,22 @@ static dev_t media_dev_t;
>  static DEFINE_MUTEX(media_devnode_lock);
>  static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
>  
> -/* Called when the last user of the media device exits. */
> -static void media_devnode_release(struct device *cd)
> +static void media_devnode_free_minor(unsigned int minor)
>  {
> -	struct media_devnode *devnode = to_media_devnode(cd);
> -
>  	mutex_lock(&media_devnode_lock);
>  
>  	/* Mark device node number as free */
> -	clear_bit(devnode->minor, media_devnode_nums);
> +	clear_bit(minor, media_devnode_nums);
>  
>  	mutex_unlock(&media_devnode_lock);
> +}
> +
> +/* Called when the last user of the media device exits. */
> +static void media_devnode_release(struct device *cd)
> +{
> +	struct media_devnode *devnode = to_media_devnode(cd);
> +
> +	media_devnode_free_minor(devnode->minor);
>  
>  	/* Release media_devnode and perform other cleanups as needed. */
>  	if (devnode->release)
> @@ -254,9 +259,7 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
>  	return 0;
>  
>  cdev_add_error:
> -	mutex_lock(&media_devnode_lock);
> -	clear_bit(devnode->minor, media_devnode_nums);
> -	mutex_unlock(&media_devnode_lock);
> +	media_devnode_free_minor(devnode->minor);
>  
>  	return ret;
>  }


^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 15/29] media: mc: Unassign minor only if it has been assigned
  2023-12-20 10:36 ` [PATCH v2 15/29] media: mc: Unassign minor only if it has been assigned Sakari Ailus
@ 2024-02-05 14:48   ` Hans Verkuil
  2024-02-07 10:58   ` Laurent Pinchart
  1 sibling, 0 replies; 92+ messages in thread
From: Hans Verkuil @ 2024-02-05 14:48 UTC (permalink / raw)
  To: Sakari Ailus, linux-media; +Cc: laurent.pinchart

On 20/12/2023 11:36, Sakari Ailus wrote:
> Assign the media device minor to -1 if it has not been previously
> assigned. There's no dependence to this yes but it will be required by

yes -> yet

> patch "media: mc: Implement best effort media device removal safety sans
> refcount" soon.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>

After fixing that somewhat confusing typo above:

Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>

> ---
>  drivers/media/mc/mc-devnode.c | 9 ++++++++-
>  include/media/media-devnode.h | 2 +-
>  2 files changed, 9 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> index 717408791a7c..5057c48f8870 100644
> --- a/drivers/media/mc/mc-devnode.c
> +++ b/drivers/media/mc/mc-devnode.c
> @@ -59,7 +59,8 @@ static void media_devnode_release(struct device *cd)
>  {
>  	struct media_devnode *devnode = to_media_devnode(cd);
>  
> -	media_devnode_free_minor(devnode->minor);
> +	if (devnode->minor != -1)
> +		media_devnode_free_minor(devnode->minor);
>  
>  	/* Release media_devnode and perform other cleanups as needed. */
>  	if (devnode->release)
> @@ -212,6 +213,7 @@ static const struct file_operations media_devnode_fops = {
>  void media_devnode_init(struct media_devnode *devnode)
>  {
>  	device_initialize(&devnode->dev);
> +	devnode->minor = -1;
>  }
>  
>  int __must_check media_devnode_register(struct media_devnode *devnode,
> @@ -220,6 +222,9 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
>  	int minor;
>  	int ret;
>  
> +	if (devnode->minor != -1)
> +		return -EINVAL;
> +
>  	/* Part 1: Find a free minor number */
>  	mutex_lock(&media_devnode_lock);
>  	minor = find_first_zero_bit(media_devnode_nums, MEDIA_NUM_DEVICES);
> @@ -261,6 +266,8 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
>  cdev_add_error:
>  	media_devnode_free_minor(devnode->minor);
>  
> +	devnode->minor = -1;
> +
>  	return ret;
>  }
>  
> diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
> index 6d46c658be21..d050f54f252a 100644
> --- a/include/media/media-devnode.h
> +++ b/include/media/media-devnode.h
> @@ -60,7 +60,7 @@ struct media_file_operations {
>   * @dev:	pointer to struct &device containing the media controller device
>   * @cdev:	struct cdev pointer character device
>   * @parent:	parent device
> - * @minor:	device node minor number
> + * @minor:	device node minor number, -1 if unassigned
>   * @flags:	flags, combination of the ``MEDIA_FLAG_*`` constants
>   * @release:	release callback called at the end of ``media_devnode_release()``
>   *		routine at media-device.c.


^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device
  2023-12-20 10:37 ` [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device Sakari Ailus
@ 2024-02-05 14:56   ` Hans Verkuil
  2024-02-07 11:13     ` Laurent Pinchart
  2024-02-21 10:40     ` Sakari Ailus
  0 siblings, 2 replies; 92+ messages in thread
From: Hans Verkuil @ 2024-02-05 14:56 UTC (permalink / raw)
  To: Sakari Ailus, linux-media; +Cc: laurent.pinchart

On 20/12/2023 11:37, Sakari Ailus wrote:
> The video device depends on the existence of its media device --- if there
> is one. Acquire a reference to it.
> 
> Note that when the media device release callback is used, then the V4L2
> device release callback is ignored and a warning is issued if both are
> set.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> ---
>  drivers/media/v4l2-core/v4l2-dev.c | 51 ++++++++++++++++++++----------
>  1 file changed, 34 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> index d13954bd31fd..c1e4995eaf5c 100644
> --- a/drivers/media/v4l2-core/v4l2-dev.c
> +++ b/drivers/media/v4l2-core/v4l2-dev.c
> @@ -176,6 +176,11 @@ static void v4l2_device_release(struct device *cd)
>  {
>  	struct video_device *vdev = to_video_device(cd);
>  	struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
> +	bool v4l2_dev_has_release = v4l2_dev->release;
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	struct media_device *mdev = v4l2_dev->mdev;
> +	bool mdev_has_release = mdev && mdev->ops && mdev->ops->release;
> +#endif
>  
>  	mutex_lock(&videodev_lock);
>  	if (WARN_ON(video_devices[vdev->minor] != vdev)) {
> @@ -198,8 +203,8 @@ static void v4l2_device_release(struct device *cd)
>  
>  	mutex_unlock(&videodev_lock);
>  
> -#if defined(CONFIG_MEDIA_CONTROLLER)
> -	if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	if (mdev && vdev->vfl_dir != VFL_DIR_M2M) {
>  		/* Remove interfaces and interface links */
>  		media_devnode_remove(vdev->intf_devnode);
>  		if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
> @@ -207,23 +212,31 @@ static void v4l2_device_release(struct device *cd)
>  	}
>  #endif
>  
> -	/* Do not call v4l2_device_put if there is no release callback set.
> -	 * Drivers that have no v4l2_device release callback might free the
> -	 * v4l2_dev instance in the video_device release callback below, so we
> -	 * must perform this check here.
> -	 *
> -	 * TODO: In the long run all drivers that use v4l2_device should use the
> -	 * v4l2_device release callback. This check will then be unnecessary.
> -	 */
> -	if (v4l2_dev->release == NULL)
> -		v4l2_dev = NULL;
> -
>  	/* Release video_device and perform other
>  	   cleanups as needed. */
>  	vdev->release(vdev);
>  
> -	/* Decrease v4l2_device refcount */
> -	if (v4l2_dev)
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	if (mdev)
> +		media_device_put(mdev);
> +
> +	/*
> +	 * Generally both struct media_device and struct v4l2_device are
> +	 * embedded in the same driver's context struct so having a release
> +	 * callback in both is a bug.
> +	 */
> +	WARN_ON(v4l2_dev_has_release && mdev_has_release);

How about:

	if (WARN_ON(v4l2_dev_has_release && mdev_has_release))
		v4l2_dev_has_release = false;

> +#endif
> +
> +	/*
> +	 * Decrease v4l2_device refcount, but only if the media device doesn't
> +	 * have a release callback.
> +	 */
> +	if (v4l2_dev_has_release
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	    && !mdev_has_release
> +#endif
> +	    )

Then this change is no longer needed.

General question: do we have drivers today that set both release functions?
Because that would now cause a WARN in the kernel log with this patch.

>  		v4l2_device_put(v4l2_dev);
>  }
>  
> @@ -792,11 +805,14 @@ static int video_register_media_controller(struct video_device *vdev)
>  	u32 intf_type;
>  	int ret;
>  
> -	/* Memory-to-memory devices are more complex and use
> +	/*
> +	 * Memory-to-memory devices are more complex and use
>  	 * their own function to register its mc entities.
>  	 */
> -	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M)
> +	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M) {
> +		media_device_get(vdev->v4l2_dev->mdev);
>  		return 0;
> +	}
>  
>  	vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
>  	vdev->entity.function = MEDIA_ENT_F_UNKNOWN;
> @@ -875,6 +891,7 @@ static int video_register_media_controller(struct video_device *vdev)
>  
>  	/* FIXME: how to create the other interface links? */
>  
> +	media_device_get(vdev->v4l2_dev->mdev);
>  #endif
>  	return 0;
>  }

Regards,

	Hans

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 21/29] media: ipu3-cio2: Request IRQ earlier
  2023-12-20 10:37 ` [PATCH v2 21/29] media: ipu3-cio2: Request IRQ earlier Sakari Ailus
@ 2024-02-05 14:58   ` Hans Verkuil
  2024-02-07 14:34     ` Laurent Pinchart
  0 siblings, 1 reply; 92+ messages in thread
From: Hans Verkuil @ 2024-02-05 14:58 UTC (permalink / raw)
  To: Sakari Ailus, linux-media; +Cc: laurent.pinchart

On 20/12/2023 11:37, Sakari Ailus wrote:
> Call devm_request_irq() before registering the async notifier, as otherwise
> it would be possible to use the device before the interrupts could be
> deliveted to the driver.

deliveted -> delivered

Isn't this a regular fix? Ditto for the previous patch (20/29).

I'd just queue this up in the next PR.

Regards,

	Hans

> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> ---
>  drivers/media/pci/intel/ipu3/ipu3-cio2.c | 10 +++++-----
>  1 file changed, 5 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> index da82d09b46ab..3222ec5b8345 100644
> --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> @@ -1789,11 +1789,6 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
>  
>  	v4l2_async_nf_init(&cio2->notifier, &cio2->v4l2_dev);
>  
> -	/* Register notifier for subdevices we care */
> -	r = cio2_parse_firmware(cio2);
> -	if (r)
> -		goto fail_clean_notifier;
> -
>  	r = devm_request_irq(dev, pci_dev->irq, cio2_irq, IRQF_SHARED,
>  			     CIO2_NAME, cio2);
>  	if (r) {
> @@ -1801,6 +1796,11 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
>  		goto fail_clean_notifier;
>  	}
>  
> +	/* Register notifier for subdevices we care */
> +	r = cio2_parse_firmware(cio2);
> +	if (r)
> +		goto fail_clean_notifier;
> +
>  	pm_runtime_put_noidle(dev);
>  	pm_runtime_allow(dev);
>  


^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 23/29] media: vimc: Release resources on media device release
  2023-12-20 10:37 ` [PATCH v2 23/29] media: vimc: Release resources on media device release Sakari Ailus
@ 2024-02-05 15:02   ` Hans Verkuil
  2024-02-07 14:38     ` Laurent Pinchart
  2024-02-21 10:53     ` Sakari Ailus
  0 siblings, 2 replies; 92+ messages in thread
From: Hans Verkuil @ 2024-02-05 15:02 UTC (permalink / raw)
  To: Sakari Ailus, linux-media; +Cc: laurent.pinchart

On 20/12/2023 11:37, Sakari Ailus wrote:
> Release all the resources when the media device is related, moving away
> form the struct v4l2_device used for that purpose.

form -> from

> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> ---
>  drivers/media/test-drivers/vimc/vimc-core.c | 15 +++++++++------
>  1 file changed, 9 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c
> index af127476e920..3e59f8c256c7 100644
> --- a/drivers/media/test-drivers/vimc/vimc-core.c
> +++ b/drivers/media/test-drivers/vimc/vimc-core.c
> @@ -264,13 +264,12 @@ static int vimc_add_subdevs(struct vimc_device *vimc)
>  	return 0;
>  }
>  
> -static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev)
> +static void vimc_mdev_release(struct media_device *mdev)
>  {
>  	struct vimc_device *vimc =
> -		container_of(v4l2_dev, struct vimc_device, v4l2_dev);
> +		container_of_const(mdev, struct vimc_device, mdev);

Why this change?

>  
>  	vimc_release_subdevs(vimc);
> -	media_device_cleanup(&vimc->mdev);
>  	kfree(vimc->ent_devs);
>  	kfree(vimc);
>  }
> @@ -336,6 +335,10 @@ static int vimc_register_devices(struct vimc_device *vimc)
>  	return ret;
>  }
>  
> +static const struct media_device_ops vimc_mdev_ops = {
> +	.release = vimc_mdev_release,
> +};
> +
>  static int vimc_probe(struct platform_device *pdev)
>  {
>  	const struct font_desc *font = find_font("VGA8x16");
> @@ -369,12 +372,12 @@ static int vimc_probe(struct platform_device *pdev)
>  	snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info),
>  		 "platform:%s", VIMC_PDEV_NAME);
>  	vimc->mdev.dev = &pdev->dev;
> +	vimc->mdev.ops = &vimc_mdev_ops;
>  	media_device_init(&vimc->mdev);
>  
>  	ret = vimc_register_devices(vimc);
>  	if (ret) {
> -		media_device_cleanup(&vimc->mdev);
> -		kfree(vimc);
> +		media_device_put(&vimc->mdev);
>  		return ret;
>  	}
>  	/*
> @@ -382,7 +385,6 @@ static int vimc_probe(struct platform_device *pdev)
>  	 * if the registration fails, we release directly from probe
>  	 */
>  
> -	vimc->v4l2_dev.release = vimc_v4l2_dev_release;
>  	platform_set_drvdata(pdev, vimc);
>  	return 0;
>  }
> @@ -397,6 +399,7 @@ static void vimc_remove(struct platform_device *pdev)
>  	media_device_unregister(&vimc->mdev);
>  	v4l2_device_unregister(&vimc->v4l2_dev);
>  	v4l2_device_put(&vimc->v4l2_dev);
> +	media_device_put(&vimc->mdev);
>  }
>  
>  static void vimc_dev_release(struct device *dev)

Regards,

	Hans

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 24/29] media: Documentation: Document how Media device resources are released
  2023-12-20 10:37 ` [PATCH v2 24/29] media: Documentation: Document how Media device resources are released Sakari Ailus
@ 2024-02-05 15:04   ` Hans Verkuil
  2024-02-07 14:43   ` Laurent Pinchart
  1 sibling, 0 replies; 92+ messages in thread
From: Hans Verkuil @ 2024-02-05 15:04 UTC (permalink / raw)
  To: Sakari Ailus, linux-media; +Cc: laurent.pinchart

On 20/12/2023 11:37, Sakari Ailus wrote:
> Document that after unregistering, Media device memory resources are
> released by the release() callback rather than by calling
> media_device_cleanup().
> 
> Also add that driver memory resources should be bound to the Media device,
> not V4L2 device.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>

Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>

> ---
>  Documentation/driver-api/media/mc-core.rst | 18 ++++++++++++++++--
>  include/media/media-device.h               |  6 ++++--
>  2 files changed, 20 insertions(+), 4 deletions(-)
> 
> diff --git a/Documentation/driver-api/media/mc-core.rst b/Documentation/driver-api/media/mc-core.rst
> index 2456950ce8ff..346f67760671 100644
> --- a/Documentation/driver-api/media/mc-core.rst
> +++ b/Documentation/driver-api/media/mc-core.rst
> @@ -46,13 +46,27 @@ Drivers initialise media device instances by calling
>  :c:func:`media_device_init()`. After initialising a media device instance, it is
>  registered by calling :c:func:`__media_device_register()` via the macro
>  ``media_device_register()`` and unregistered by calling
> -:c:func:`media_device_unregister()`. An initialised media device must be
> -eventually cleaned up by calling :c:func:`media_device_cleanup()`.
> +:c:func:`media_device_unregister()`. The resources of an unregistered media
> +device will be released by the ``release()`` callback of :c:type:`media_device`
> +ops, which will be called when the last user of the media device has released it
> +calling :c:func:`media_device_put()`.
> +
> +The ``release()`` callback is the way all the resources of the media device are
> +released once :c:func:`media_device_init()` has been called. This is also
> +relevant during device driver's probe function as the ``release()`` callback
> +will also have to be able to safely release the resources related to a partially
> +initialised media device.
>  
>  Note that it is not allowed to unregister a media device instance that was not
>  previously registered, or clean up a media device instance that was not
>  previously initialised.
>  
> +Media device and driver's per-device context
> +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> +
> +Drivers should use the struct media_device_ops ``release()`` callback to release
> +their own resources and not e.g. that of the struct v4l2_device.
> +
>  Entities
>  ^^^^^^^^
>  
> diff --git a/include/media/media-device.h b/include/media/media-device.h
> index c6816be0eee8..98e1892f1b51 100644
> --- a/include/media/media-device.h
> +++ b/include/media/media-device.h
> @@ -250,8 +250,10 @@ void media_device_init(struct media_device *mdev);
>   *
>   * @mdev:	pointer to struct &media_device
>   *
> - * This function that will destroy the graph_mutex that is
> - * initialized in media_device_init().
> + * This function that will destroy the graph_mutex that is initialized in
> + * media_device_init(). Note that *only* drivers that do not manage releasing
> + * the memory of th media device itself call this function. This function is
> + * thus effectively DEPRECATED.
>   */
>  void media_device_cleanup(struct media_device *mdev);
>  


^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 25/29] media: mc: Add per-file-handle data support
  2023-12-20 10:37 ` [PATCH v2 25/29] media: mc: Add per-file-handle data support Sakari Ailus
@ 2024-02-05 15:08   ` Hans Verkuil
  0 siblings, 0 replies; 92+ messages in thread
From: Hans Verkuil @ 2024-02-05 15:08 UTC (permalink / raw)
  To: Sakari Ailus, linux-media; +Cc: laurent.pinchart

On 20/12/2023 11:37, Sakari Ailus wrote:
> From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> 
> The media devnode core associates devnodes with files by storing the
> devnode pointer in the file structure private_data field. In order to
> allow tracking of per-file-handle data introduce a new media devnode
> file handle structure that stores the devnode pointer, and store a
> pointer to that structure in the file private_data field.
> 
> Users of the media devnode code (the only existing user being
> media_device) are responsible for managing their own subclass of the
> media_devnode_fh structure.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> 
> Prepare struct media_device_fh to be used for maintaining file handle list
> to avoid shuffling things here and there right after.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>

Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>

> ---
>  drivers/media/mc/mc-device.c  | 14 +++++++++++++-
>  drivers/media/mc/mc-devnode.c | 20 +++++++++-----------
>  include/media/media-device.h  |  7 +++++++
>  include/media/media-devnode.h | 18 +++++++++++++++++-
>  include/media/media-fh.h      | 32 ++++++++++++++++++++++++++++++++
>  5 files changed, 78 insertions(+), 13 deletions(-)
>  create mode 100644 include/media/media-fh.h
> 
> diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
> index 10426c2796b6..67a39cb63f89 100644
> --- a/drivers/media/mc/mc-device.c
> +++ b/drivers/media/mc/mc-device.c
> @@ -22,6 +22,7 @@
>  #include <media/media-device.h>
>  #include <media/media-devnode.h>
>  #include <media/media-entity.h>
> +#include <media/media-fh.h>
>  #include <media/media-request.h>
>  
>  #ifdef CONFIG_MEDIA_CONTROLLER
> @@ -35,7 +36,6 @@
>  #define MEDIA_ENT_SUBTYPE_MASK			0x0000ffff
>  #define MEDIA_ENT_T_DEVNODE_UNKNOWN		(MEDIA_ENT_F_OLD_BASE | \
>  						 MEDIA_ENT_SUBTYPE_MASK)
> -
>  /* -----------------------------------------------------------------------------
>   * Userspace API
>   */
> @@ -47,11 +47,23 @@ static inline void __user *media_get_uptr(__u64 arg)
>  
>  static int media_device_open(struct file *filp)
>  {
> +	struct media_device_fh *fh;
> +
> +	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
> +	if (!fh)
> +		return -ENOMEM;
> +
> +	filp->private_data = &fh->fh;
> +
>  	return 0;
>  }
>  
>  static int media_device_close(struct file *filp)
>  {
> +	struct media_device_fh *fh = media_device_fh(filp);
> +
> +	kfree(fh);
> +
>  	return 0;
>  }
>  
> diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> index 4ea05e42dafb..04d376015526 100644
> --- a/drivers/media/mc/mc-devnode.c
> +++ b/drivers/media/mc/mc-devnode.c
> @@ -150,6 +150,7 @@ static long media_compat_ioctl(struct file *filp, unsigned int cmd,
>  static int media_open(struct inode *inode, struct file *filp)
>  {
>  	struct media_devnode *devnode;
> +	struct media_devnode_fh *fh;
>  	int ret;
>  
>  	/* Check if the media device is available. This needs to be done with
> @@ -170,17 +171,15 @@ static int media_open(struct inode *inode, struct file *filp)
>  	get_device(&devnode->dev);
>  	mutex_unlock(&media_devnode_lock);
>  
> -	filp->private_data = devnode;
> -
> -	if (devnode->fops->open) {
> -		ret = devnode->fops->open(filp);
> -		if (ret) {
> -			put_device(&devnode->dev);
> -			filp->private_data = NULL;
> -			return ret;
> -		}
> +	ret = devnode->fops->open(filp);
> +	if (ret) {
> +		put_device(&devnode->dev);
> +		return ret;
>  	}
>  
> +	fh = filp->private_data;
> +	fh->devnode = devnode;
> +
>  	return 0;
>  }
>  
> @@ -189,8 +188,7 @@ static int media_release(struct inode *inode, struct file *filp)
>  {
>  	struct media_devnode *devnode = media_devnode_data(filp);
>  
> -	if (devnode->fops->release)
> -		devnode->fops->release(filp);
> +	devnode->fops->release(filp);
>  
>  	filp->private_data = NULL;
>  
> diff --git a/include/media/media-device.h b/include/media/media-device.h
> index 98e1892f1b51..83b8ea44463d 100644
> --- a/include/media/media-device.h
> +++ b/include/media/media-device.h
> @@ -110,6 +110,10 @@ struct media_device_ops {
>   *		     other operations that stop or start streaming.
>   * @request_id: Used to generate unique request IDs
>   *
> + * @fh_list:	List of file handles in the media device
> + *		(struct media_device_fh.mdev_list).
> + * @fh_list_lock: Serialise access to fh_list list.
> + *
>   * This structure represents an abstract high-level media device. It allows easy
>   * access to entities and provides basic media device-level support. The
>   * structure can be allocated directly or embedded in a larger structure.
> @@ -182,6 +186,9 @@ struct media_device {
>  
>  	struct mutex req_queue_mutex;
>  	atomic_t request_id;
> +
> +	struct list_head fh_list;
> +	spinlock_t fh_list_lock;
>  };
>  
>  /* We don't need to include usb.h here */
> diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
> index d050f54f252a..b0efdde4ffd8 100644
> --- a/include/media/media-devnode.h
> +++ b/include/media/media-devnode.h
> @@ -53,6 +53,20 @@ struct media_file_operations {
>  	int (*release) (struct file *);
>  };
>  
> +/**
> + * struct media_devnode_fh - Media device node file handle
> + * @devnode:	pointer to the media device node
> + *
> + * This structure serves as a base for per-file-handle data storage. Media
> + * device node users embed media_devnode_fh in their custom file handle data
> + * structures and store the media_devnode_fh in the file private_data in order
> + * to let the media device node core locate the media_devnode corresponding to a
> + * file handle.
> + */
> +struct media_devnode_fh {
> +	struct media_devnode *devnode;
> +};
> +
>  /**
>   * struct media_devnode - Media device node
>   * @media_dev:	pointer to struct &media_device
> @@ -137,7 +151,9 @@ void media_devnode_unregister(struct media_devnode *devnode);
>   */
>  static inline struct media_devnode *media_devnode_data(struct file *filp)
>  {
> -	return filp->private_data;
> +	struct media_devnode_fh *fh = filp->private_data;
> +
> +	return fh->devnode;
>  }
>  
>  /**
> diff --git a/include/media/media-fh.h b/include/media/media-fh.h
> new file mode 100644
> index 000000000000..6f00744b81d6
> --- /dev/null
> +++ b/include/media/media-fh.h
> @@ -0,0 +1,32 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Media device file handle
> + *
> + * Copyright (C) 2019--2023 Intel Corporation
> + */
> +
> +#ifndef MEDIA_FH_H
> +#define MEDIA_FH_H
> +
> +#include <linux/list.h>
> +#include <linux/file.h>
> +
> +#include <media/media-devnode.h>
> +
> +/**
> + * struct media_device_fh - File handle specific information on MC
> + *
> + * @fh: The media device file handle
> + * @mdev_list: This file handle in media device's list of file handles
> + */
> +struct media_device_fh {
> +	struct media_devnode_fh fh;
> +	struct list_head mdev_list;
> +};
> +
> +static inline struct media_device_fh *media_device_fh(struct file *filp)
> +{
> +	return container_of(filp->private_data, struct media_device_fh, fh);
> +}
> +
> +#endif /* MEDIA_FH_H */


^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 26/29] media: mc: Maintain a list of open file handles in a media device
  2023-12-20 10:37 ` [PATCH v2 26/29] media: mc: Maintain a list of open file handles in a media device Sakari Ailus
@ 2024-02-05 15:11   ` Hans Verkuil
  2024-02-05 15:16     ` Laurent Pinchart
  0 siblings, 1 reply; 92+ messages in thread
From: Hans Verkuil @ 2024-02-05 15:11 UTC (permalink / raw)
  To: Sakari Ailus, linux-media; +Cc: laurent.pinchart

On 20/12/2023 11:37, Sakari Ailus wrote:
> The list of file handles is needed to deliver media events as well as for
> other purposes in the future.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> ---
>  drivers/media/mc/mc-device.c  | 23 ++++++++++++++++++++++-
>  drivers/media/mc/mc-devnode.c |  2 +-
>  include/media/media-devnode.h |  4 +++-
>  3 files changed, 26 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
> index 67a39cb63f89..9cc055deeec9 100644
> --- a/drivers/media/mc/mc-device.c
> +++ b/drivers/media/mc/mc-device.c
> @@ -45,9 +45,11 @@ static inline void __user *media_get_uptr(__u64 arg)
>  	return (void __user *)(uintptr_t)arg;
>  }
>  
> -static int media_device_open(struct file *filp)
> +static int media_device_open(struct media_devnode *devnode, struct file *filp)
>  {
> +	struct media_device *mdev = to_media_device(devnode);
>  	struct media_device_fh *fh;
> +	unsigned long flags;
>  
>  	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
>  	if (!fh)
> @@ -55,12 +57,23 @@ static int media_device_open(struct file *filp)
>  
>  	filp->private_data = &fh->fh;
>  
> +	spin_lock_irqsave(&mdev->fh_list_lock, flags);

The only reason for using the irqsave variant is because we want
to support events in the future, and those can be sent in irq context.

But it is confusing to see this being used here when it is not yet
needed.

At minimum this should be explained in the commit log.

Regards,

	Hans

> +	list_add(&fh->mdev_list, &mdev->fh_list);
> +	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
> +
>  	return 0;
>  }
>  
>  static int media_device_close(struct file *filp)
>  {
> +	struct media_devnode *devnode = media_devnode_data(filp);
> +	struct media_device *mdev = to_media_device(devnode);
>  	struct media_device_fh *fh = media_device_fh(filp);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&mdev->fh_list_lock, flags);
> +	list_del(&fh->mdev_list);
> +	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
>  
>  	kfree(fh);
>  
> @@ -769,11 +782,13 @@ void media_device_init(struct media_device *mdev)
>  	INIT_LIST_HEAD(&mdev->pads);
>  	INIT_LIST_HEAD(&mdev->links);
>  	INIT_LIST_HEAD(&mdev->entity_notify);
> +	INIT_LIST_HEAD(&mdev->fh_list);
>  
>  	mutex_init(&mdev->req_queue_mutex);
>  	mutex_init(&mdev->graph_mutex);
>  	ida_init(&mdev->entity_internal_idx);
>  	atomic_set(&mdev->request_id, 0);
> +	spin_lock_init(&mdev->fh_list_lock);
>  
>  	mdev->devnode.release = media_device_release;
>  	media_devnode_init(&mdev->devnode);
> @@ -824,6 +839,8 @@ EXPORT_SYMBOL_GPL(__media_device_register);
>  
>  void media_device_unregister(struct media_device *mdev)
>  {
> +	unsigned long flags;
> +
>  	if (mdev == NULL)
>  		return;
>  
> @@ -834,6 +851,10 @@ void media_device_unregister(struct media_device *mdev)
>  	}
>  	mutex_unlock(&mdev->graph_mutex);
>  
> +	spin_lock_irqsave(&mdev->fh_list_lock, flags);
> +	list_del_init(&mdev->fh_list);
> +	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
> +
>  	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
>  	dev_dbg(mdev->dev, "Media device unregistering\n");
>  	media_devnode_unregister(&mdev->devnode);
> diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> index 04d376015526..0b5c24828e24 100644
> --- a/drivers/media/mc/mc-devnode.c
> +++ b/drivers/media/mc/mc-devnode.c
> @@ -171,7 +171,7 @@ static int media_open(struct inode *inode, struct file *filp)
>  	get_device(&devnode->dev);
>  	mutex_unlock(&media_devnode_lock);
>  
> -	ret = devnode->fops->open(filp);
> +	ret = devnode->fops->open(devnode, filp);
>  	if (ret) {
>  		put_device(&devnode->dev);
>  		return ret;
> diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
> index b0efdde4ffd8..840f7ae852d3 100644
> --- a/include/media/media-devnode.h
> +++ b/include/media/media-devnode.h
> @@ -21,6 +21,8 @@
>  #include <linux/device.h>
>  #include <linux/cdev.h>
>  
> +struct media_devnode;
> +
>  /*
>   * Flag to mark the media_devnode struct as registered. Drivers must not touch
>   * this flag directly, it will be set and cleared by media_devnode_register and
> @@ -49,7 +51,7 @@ struct media_file_operations {
>  	__poll_t (*poll) (struct file *, struct poll_table_struct *);
>  	long (*ioctl) (struct file *, unsigned int, unsigned long);
>  	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
> -	int (*open) (struct file *);
> +	int (*open) (struct media_devnode *, struct file *);
>  	int (*release) (struct file *);
>  };
>  


^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 26/29] media: mc: Maintain a list of open file handles in a media device
  2024-02-05 15:11   ` Hans Verkuil
@ 2024-02-05 15:16     ` Laurent Pinchart
  2024-02-05 15:32       ` Hans Verkuil
  0 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-05 15:16 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Sakari Ailus, linux-media

On Mon, Feb 05, 2024 at 04:11:43PM +0100, Hans Verkuil wrote:
> On 20/12/2023 11:37, Sakari Ailus wrote:
> > The list of file handles is needed to deliver media events as well as for
> > other purposes in the future.
> > 
> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > ---
> >  drivers/media/mc/mc-device.c  | 23 ++++++++++++++++++++++-
> >  drivers/media/mc/mc-devnode.c |  2 +-
> >  include/media/media-devnode.h |  4 +++-
> >  3 files changed, 26 insertions(+), 3 deletions(-)
> > 
> > diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
> > index 67a39cb63f89..9cc055deeec9 100644
> > --- a/drivers/media/mc/mc-device.c
> > +++ b/drivers/media/mc/mc-device.c
> > @@ -45,9 +45,11 @@ static inline void __user *media_get_uptr(__u64 arg)
> >  	return (void __user *)(uintptr_t)arg;
> >  }
> >  
> > -static int media_device_open(struct file *filp)
> > +static int media_device_open(struct media_devnode *devnode, struct file *filp)
> >  {
> > +	struct media_device *mdev = to_media_device(devnode);
> >  	struct media_device_fh *fh;
> > +	unsigned long flags;
> >  
> >  	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
> >  	if (!fh)
> > @@ -55,12 +57,23 @@ static int media_device_open(struct file *filp)
> >  
> >  	filp->private_data = &fh->fh;
> >  
> > +	spin_lock_irqsave(&mdev->fh_list_lock, flags);
> 
> The only reason for using the irqsave variant is because we want
> to support events in the future, and those can be sent in irq context.

Even in that case, would media_device_open() ever be called from
interrupt context ? spin_lock_irqsave() is only needed if you don't know
which context the function can be called from. If we know we'll be
called from interruptible context only, you can use spin_lock_irq()
instead.

> But it is confusing to see this being used here when it is not yet
> needed.
> 
> At minimum this should be explained in the commit log.
>
> > +	list_add(&fh->mdev_list, &mdev->fh_list);
> > +	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
> > +
> >  	return 0;
> >  }
> >  
> >  static int media_device_close(struct file *filp)
> >  {
> > +	struct media_devnode *devnode = media_devnode_data(filp);
> > +	struct media_device *mdev = to_media_device(devnode);
> >  	struct media_device_fh *fh = media_device_fh(filp);
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&mdev->fh_list_lock, flags);
> > +	list_del(&fh->mdev_list);
> > +	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
> >  
> >  	kfree(fh);
> >  
> > @@ -769,11 +782,13 @@ void media_device_init(struct media_device *mdev)
> >  	INIT_LIST_HEAD(&mdev->pads);
> >  	INIT_LIST_HEAD(&mdev->links);
> >  	INIT_LIST_HEAD(&mdev->entity_notify);
> > +	INIT_LIST_HEAD(&mdev->fh_list);
> >  
> >  	mutex_init(&mdev->req_queue_mutex);
> >  	mutex_init(&mdev->graph_mutex);
> >  	ida_init(&mdev->entity_internal_idx);
> >  	atomic_set(&mdev->request_id, 0);
> > +	spin_lock_init(&mdev->fh_list_lock);
> >  
> >  	mdev->devnode.release = media_device_release;
> >  	media_devnode_init(&mdev->devnode);
> > @@ -824,6 +839,8 @@ EXPORT_SYMBOL_GPL(__media_device_register);
> >  
> >  void media_device_unregister(struct media_device *mdev)
> >  {
> > +	unsigned long flags;
> > +
> >  	if (mdev == NULL)
> >  		return;
> >  
> > @@ -834,6 +851,10 @@ void media_device_unregister(struct media_device *mdev)
> >  	}
> >  	mutex_unlock(&mdev->graph_mutex);
> >  
> > +	spin_lock_irqsave(&mdev->fh_list_lock, flags);
> > +	list_del_init(&mdev->fh_list);
> > +	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
> > +
> >  	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
> >  	dev_dbg(mdev->dev, "Media device unregistering\n");
> >  	media_devnode_unregister(&mdev->devnode);
> > diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> > index 04d376015526..0b5c24828e24 100644
> > --- a/drivers/media/mc/mc-devnode.c
> > +++ b/drivers/media/mc/mc-devnode.c
> > @@ -171,7 +171,7 @@ static int media_open(struct inode *inode, struct file *filp)
> >  	get_device(&devnode->dev);
> >  	mutex_unlock(&media_devnode_lock);
> >  
> > -	ret = devnode->fops->open(filp);
> > +	ret = devnode->fops->open(devnode, filp);
> >  	if (ret) {
> >  		put_device(&devnode->dev);
> >  		return ret;
> > diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
> > index b0efdde4ffd8..840f7ae852d3 100644
> > --- a/include/media/media-devnode.h
> > +++ b/include/media/media-devnode.h
> > @@ -21,6 +21,8 @@
> >  #include <linux/device.h>
> >  #include <linux/cdev.h>
> >  
> > +struct media_devnode;
> > +
> >  /*
> >   * Flag to mark the media_devnode struct as registered. Drivers must not touch
> >   * this flag directly, it will be set and cleared by media_devnode_register and
> > @@ -49,7 +51,7 @@ struct media_file_operations {
> >  	__poll_t (*poll) (struct file *, struct poll_table_struct *);
> >  	long (*ioctl) (struct file *, unsigned int, unsigned long);
> >  	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
> > -	int (*open) (struct file *);
> > +	int (*open) (struct media_devnode *, struct file *);
> >  	int (*release) (struct file *);
> >  };
> >  

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 26/29] media: mc: Maintain a list of open file handles in a media device
  2024-02-05 15:16     ` Laurent Pinchart
@ 2024-02-05 15:32       ` Hans Verkuil
  2024-02-05 15:41         ` Laurent Pinchart
  0 siblings, 1 reply; 92+ messages in thread
From: Hans Verkuil @ 2024-02-05 15:32 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: Sakari Ailus, linux-media

On 05/02/2024 16:16, Laurent Pinchart wrote:
> On Mon, Feb 05, 2024 at 04:11:43PM +0100, Hans Verkuil wrote:
>> On 20/12/2023 11:37, Sakari Ailus wrote:
>>> The list of file handles is needed to deliver media events as well as for
>>> other purposes in the future.
>>>
>>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
>>> ---
>>>  drivers/media/mc/mc-device.c  | 23 ++++++++++++++++++++++-
>>>  drivers/media/mc/mc-devnode.c |  2 +-
>>>  include/media/media-devnode.h |  4 +++-
>>>  3 files changed, 26 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
>>> index 67a39cb63f89..9cc055deeec9 100644
>>> --- a/drivers/media/mc/mc-device.c
>>> +++ b/drivers/media/mc/mc-device.c
>>> @@ -45,9 +45,11 @@ static inline void __user *media_get_uptr(__u64 arg)
>>>  	return (void __user *)(uintptr_t)arg;
>>>  }
>>>  
>>> -static int media_device_open(struct file *filp)
>>> +static int media_device_open(struct media_devnode *devnode, struct file *filp)
>>>  {
>>> +	struct media_device *mdev = to_media_device(devnode);
>>>  	struct media_device_fh *fh;
>>> +	unsigned long flags;
>>>  
>>>  	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
>>>  	if (!fh)
>>> @@ -55,12 +57,23 @@ static int media_device_open(struct file *filp)
>>>  
>>>  	filp->private_data = &fh->fh;
>>>  
>>> +	spin_lock_irqsave(&mdev->fh_list_lock, flags);
>>
>> The only reason for using the irqsave variant is because we want
>> to support events in the future, and those can be sent in irq context.
> 
> Even in that case, would media_device_open() ever be called from
> interrupt context ? spin_lock_irqsave() is only needed if you don't know
> which context the function can be called from. If we know we'll be
> called from interruptible context only, you can use spin_lock_irq()
> instead.

Someone can call open() while at the same time the kernel sends a
media event from interrupt context. Such an event function will walk
over the fh_list. The irqsave here is meant to ensure that no event
interrupt can run while we add our fh to the fh list.

But without interrupts that access fh_list the irqsave variant is
confusing.

Regards,

	Hans

> 
>> But it is confusing to see this being used here when it is not yet
>> needed.
>>
>> At minimum this should be explained in the commit log.
>>
>>> +	list_add(&fh->mdev_list, &mdev->fh_list);
>>> +	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
>>> +
>>>  	return 0;
>>>  }
>>>  
>>>  static int media_device_close(struct file *filp)
>>>  {
>>> +	struct media_devnode *devnode = media_devnode_data(filp);
>>> +	struct media_device *mdev = to_media_device(devnode);
>>>  	struct media_device_fh *fh = media_device_fh(filp);
>>> +	unsigned long flags;
>>> +
>>> +	spin_lock_irqsave(&mdev->fh_list_lock, flags);
>>> +	list_del(&fh->mdev_list);
>>> +	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
>>>  
>>>  	kfree(fh);
>>>  
>>> @@ -769,11 +782,13 @@ void media_device_init(struct media_device *mdev)
>>>  	INIT_LIST_HEAD(&mdev->pads);
>>>  	INIT_LIST_HEAD(&mdev->links);
>>>  	INIT_LIST_HEAD(&mdev->entity_notify);
>>> +	INIT_LIST_HEAD(&mdev->fh_list);
>>>  
>>>  	mutex_init(&mdev->req_queue_mutex);
>>>  	mutex_init(&mdev->graph_mutex);
>>>  	ida_init(&mdev->entity_internal_idx);
>>>  	atomic_set(&mdev->request_id, 0);
>>> +	spin_lock_init(&mdev->fh_list_lock);
>>>  
>>>  	mdev->devnode.release = media_device_release;
>>>  	media_devnode_init(&mdev->devnode);
>>> @@ -824,6 +839,8 @@ EXPORT_SYMBOL_GPL(__media_device_register);
>>>  
>>>  void media_device_unregister(struct media_device *mdev)
>>>  {
>>> +	unsigned long flags;
>>> +
>>>  	if (mdev == NULL)
>>>  		return;
>>>  
>>> @@ -834,6 +851,10 @@ void media_device_unregister(struct media_device *mdev)
>>>  	}
>>>  	mutex_unlock(&mdev->graph_mutex);
>>>  
>>> +	spin_lock_irqsave(&mdev->fh_list_lock, flags);
>>> +	list_del_init(&mdev->fh_list);
>>> +	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
>>> +
>>>  	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
>>>  	dev_dbg(mdev->dev, "Media device unregistering\n");
>>>  	media_devnode_unregister(&mdev->devnode);
>>> diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
>>> index 04d376015526..0b5c24828e24 100644
>>> --- a/drivers/media/mc/mc-devnode.c
>>> +++ b/drivers/media/mc/mc-devnode.c
>>> @@ -171,7 +171,7 @@ static int media_open(struct inode *inode, struct file *filp)
>>>  	get_device(&devnode->dev);
>>>  	mutex_unlock(&media_devnode_lock);
>>>  
>>> -	ret = devnode->fops->open(filp);
>>> +	ret = devnode->fops->open(devnode, filp);
>>>  	if (ret) {
>>>  		put_device(&devnode->dev);
>>>  		return ret;
>>> diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
>>> index b0efdde4ffd8..840f7ae852d3 100644
>>> --- a/include/media/media-devnode.h
>>> +++ b/include/media/media-devnode.h
>>> @@ -21,6 +21,8 @@
>>>  #include <linux/device.h>
>>>  #include <linux/cdev.h>
>>>  
>>> +struct media_devnode;
>>> +
>>>  /*
>>>   * Flag to mark the media_devnode struct as registered. Drivers must not touch
>>>   * this flag directly, it will be set and cleared by media_devnode_register and
>>> @@ -49,7 +51,7 @@ struct media_file_operations {
>>>  	__poll_t (*poll) (struct file *, struct poll_table_struct *);
>>>  	long (*ioctl) (struct file *, unsigned int, unsigned long);
>>>  	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
>>> -	int (*open) (struct file *);
>>> +	int (*open) (struct media_devnode *, struct file *);
>>>  	int (*release) (struct file *);
>>>  };
>>>  
> 


^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 26/29] media: mc: Maintain a list of open file handles in a media device
  2024-02-05 15:32       ` Hans Verkuil
@ 2024-02-05 15:41         ` Laurent Pinchart
  2024-02-21 11:53           ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-05 15:41 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Sakari Ailus, linux-media

On Mon, Feb 05, 2024 at 04:32:44PM +0100, Hans Verkuil wrote:
> On 05/02/2024 16:16, Laurent Pinchart wrote:
> > On Mon, Feb 05, 2024 at 04:11:43PM +0100, Hans Verkuil wrote:
> >> On 20/12/2023 11:37, Sakari Ailus wrote:
> >>> The list of file handles is needed to deliver media events as well as for
> >>> other purposes in the future.
> >>>
> >>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> >>> ---
> >>>  drivers/media/mc/mc-device.c  | 23 ++++++++++++++++++++++-
> >>>  drivers/media/mc/mc-devnode.c |  2 +-
> >>>  include/media/media-devnode.h |  4 +++-
> >>>  3 files changed, 26 insertions(+), 3 deletions(-)
> >>>
> >>> diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
> >>> index 67a39cb63f89..9cc055deeec9 100644
> >>> --- a/drivers/media/mc/mc-device.c
> >>> +++ b/drivers/media/mc/mc-device.c
> >>> @@ -45,9 +45,11 @@ static inline void __user *media_get_uptr(__u64 arg)
> >>>  	return (void __user *)(uintptr_t)arg;
> >>>  }
> >>>  
> >>> -static int media_device_open(struct file *filp)
> >>> +static int media_device_open(struct media_devnode *devnode, struct file *filp)
> >>>  {
> >>> +	struct media_device *mdev = to_media_device(devnode);
> >>>  	struct media_device_fh *fh;
> >>> +	unsigned long flags;
> >>>  
> >>>  	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
> >>>  	if (!fh)
> >>> @@ -55,12 +57,23 @@ static int media_device_open(struct file *filp)
> >>>  
> >>>  	filp->private_data = &fh->fh;
> >>>  
> >>> +	spin_lock_irqsave(&mdev->fh_list_lock, flags);
> >>
> >> The only reason for using the irqsave variant is because we want
> >> to support events in the future, and those can be sent in irq context.
> > 
> > Even in that case, would media_device_open() ever be called from
> > interrupt context ? spin_lock_irqsave() is only needed if you don't know
> > which context the function can be called from. If we know we'll be
> > called from interruptible context only, you can use spin_lock_irq()
> > instead.
> 
> Someone can call open() while at the same time the kernel sends a
> media event from interrupt context. Such an event function will walk
> over the fh_list. The irqsave here is meant to ensure that no event
> interrupt can run while we add our fh to the fh list.

You don't need spin_lock_irqsave() for that, spin_lock_irq() is enough.
In your interrupt handler, you need spin_lock() only.
spin_lock_irqsave() is for places that can be called both from
interruptible and non-interruptible contexts.

> But without interrupts that access fh_list the irqsave variant is
> confusing.
> 
> >> But it is confusing to see this being used here when it is not yet
> >> needed.
> >>
> >> At minimum this should be explained in the commit log.
> >>
> >>> +	list_add(&fh->mdev_list, &mdev->fh_list);
> >>> +	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
> >>> +
> >>>  	return 0;
> >>>  }
> >>>  
> >>>  static int media_device_close(struct file *filp)
> >>>  {
> >>> +	struct media_devnode *devnode = media_devnode_data(filp);
> >>> +	struct media_device *mdev = to_media_device(devnode);
> >>>  	struct media_device_fh *fh = media_device_fh(filp);
> >>> +	unsigned long flags;
> >>> +
> >>> +	spin_lock_irqsave(&mdev->fh_list_lock, flags);
> >>> +	list_del(&fh->mdev_list);
> >>> +	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
> >>>  
> >>>  	kfree(fh);
> >>>  
> >>> @@ -769,11 +782,13 @@ void media_device_init(struct media_device *mdev)
> >>>  	INIT_LIST_HEAD(&mdev->pads);
> >>>  	INIT_LIST_HEAD(&mdev->links);
> >>>  	INIT_LIST_HEAD(&mdev->entity_notify);
> >>> +	INIT_LIST_HEAD(&mdev->fh_list);
> >>>  
> >>>  	mutex_init(&mdev->req_queue_mutex);
> >>>  	mutex_init(&mdev->graph_mutex);
> >>>  	ida_init(&mdev->entity_internal_idx);
> >>>  	atomic_set(&mdev->request_id, 0);
> >>> +	spin_lock_init(&mdev->fh_list_lock);
> >>>  
> >>>  	mdev->devnode.release = media_device_release;
> >>>  	media_devnode_init(&mdev->devnode);
> >>> @@ -824,6 +839,8 @@ EXPORT_SYMBOL_GPL(__media_device_register);
> >>>  
> >>>  void media_device_unregister(struct media_device *mdev)
> >>>  {
> >>> +	unsigned long flags;
> >>> +
> >>>  	if (mdev == NULL)
> >>>  		return;
> >>>  
> >>> @@ -834,6 +851,10 @@ void media_device_unregister(struct media_device *mdev)
> >>>  	}
> >>>  	mutex_unlock(&mdev->graph_mutex);
> >>>  
> >>> +	spin_lock_irqsave(&mdev->fh_list_lock, flags);
> >>> +	list_del_init(&mdev->fh_list);
> >>> +	spin_unlock_irqrestore(&mdev->fh_list_lock, flags);
> >>> +
> >>>  	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
> >>>  	dev_dbg(mdev->dev, "Media device unregistering\n");
> >>>  	media_devnode_unregister(&mdev->devnode);
> >>> diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> >>> index 04d376015526..0b5c24828e24 100644
> >>> --- a/drivers/media/mc/mc-devnode.c
> >>> +++ b/drivers/media/mc/mc-devnode.c
> >>> @@ -171,7 +171,7 @@ static int media_open(struct inode *inode, struct file *filp)
> >>>  	get_device(&devnode->dev);
> >>>  	mutex_unlock(&media_devnode_lock);
> >>>  
> >>> -	ret = devnode->fops->open(filp);
> >>> +	ret = devnode->fops->open(devnode, filp);
> >>>  	if (ret) {
> >>>  		put_device(&devnode->dev);
> >>>  		return ret;
> >>> diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
> >>> index b0efdde4ffd8..840f7ae852d3 100644
> >>> --- a/include/media/media-devnode.h
> >>> +++ b/include/media/media-devnode.h
> >>> @@ -21,6 +21,8 @@
> >>>  #include <linux/device.h>
> >>>  #include <linux/cdev.h>
> >>>  
> >>> +struct media_devnode;
> >>> +
> >>>  /*
> >>>   * Flag to mark the media_devnode struct as registered. Drivers must not touch
> >>>   * this flag directly, it will be set and cleared by media_devnode_register and
> >>> @@ -49,7 +51,7 @@ struct media_file_operations {
> >>>  	__poll_t (*poll) (struct file *, struct poll_table_struct *);
> >>>  	long (*ioctl) (struct file *, unsigned int, unsigned long);
> >>>  	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
> >>> -	int (*open) (struct file *);
> >>> +	int (*open) (struct media_devnode *, struct file *);
> >>>  	int (*release) (struct file *);
> >>>  };
> >>>  

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 04/29] media: mc: utilize new cdev_device_add helper function
  2023-12-20 10:36 ` [PATCH v2 04/29] media: mc: utilize new cdev_device_add helper function Sakari Ailus
@ 2024-02-07  9:38   ` Laurent Pinchart
  2024-02-07  9:51     ` Laurent Pinchart
  0 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07  9:38 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:36:48PM +0200, Sakari Ailus wrote:
> From: Logan Gunthorpe <logang@deltatee.com>
> 
> Replace the open coded registration of the cdev and dev with the
> new device_add_cdev() helper. The helper replaces a common pattern by
> taking the proper reference against the parent device and adding both
> the cdev and the device.
> 
> Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
> Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

This reapplies a commit you've reverted in 02/29 in this series. I
understand this is done to be able to apply the revert in 03/29 cleanly.
Given that those three patches are consecutive, wouldn't it be better to
squash 02/29, 03/29 and 04/29, with the commit message of 03/29 ?
Otherwise, I would at least drop the Acked-by and Reviewed-by tags in
the patches you reapply, as they've been reviewed in a different
context.

The same applies to patches 05/29, 06/29 and 07/29.

> ---
>  drivers/media/cec/core/cec-core.c | 16 ++++------------
>  drivers/media/mc/mc-devnode.c     | 23 +++++++++--------------
>  2 files changed, 13 insertions(+), 26 deletions(-)
> 
> diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
> index 0645e68411fb..15494b46458a 100644
> --- a/drivers/media/cec/core/cec-core.c
> +++ b/drivers/media/cec/core/cec-core.c
> @@ -137,26 +137,19 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode,
>  
>  	/* Part 2: Initialize and register the character device */
>  	cdev_init(&devnode->cdev, &cec_devnode_fops);
> -	devnode->cdev.kobj.parent = &devnode->dev.kobj;
>  	devnode->cdev.owner = owner;
>  	kobject_set_name(&devnode->cdev.kobj, "cec%d", devnode->minor);
>  
>  	devnode->registered = true;
> -	ret = cdev_add(&devnode->cdev, devnode->dev.devt, 1);
> -	if (ret < 0) {
> -		pr_err("%s: cdev_add failed\n", __func__);
> +	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
> +	if (ret) {
> +		pr_err("%s: cdev_device_add failed\n", __func__);
>  		devnode->registered = false;
>  		goto clr_bit;
>  	}
>  
> -	ret = device_add(&devnode->dev);
> -	if (ret)
> -		goto cdev_del;
> -
>  	return 0;
>  
> -cdev_del:
> -	cdev_del(&devnode->cdev);
>  clr_bit:
>  	mutex_lock(&cec_devnode_lock);
>  	clear_bit(devnode->minor, cec_devnode_nums);
> @@ -202,8 +195,7 @@ static void cec_devnode_unregister(struct cec_adapter *adap)
>  	cec_adap_enable(adap);
>  	mutex_unlock(&adap->lock);
>  
> -	device_del(&devnode->dev);
> -	cdev_del(&devnode->cdev);
> +	cdev_device_del(&devnode->cdev, &devnode->dev);
>  	put_device(&devnode->dev);
>  }
>  
> diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> index 1e1792c3ae3f..fabcd646679b 100644
> --- a/drivers/media/mc/mc-devnode.c
> +++ b/drivers/media/mc/mc-devnode.c
> @@ -232,29 +232,24 @@ int __must_check media_devnode_register(struct media_device *mdev,
>  	devnode->minor = minor;
>  	devnode->media_dev = mdev;
>  
> -	/* Part 2: Initialize and register the character device */
> +	/* Part 2: Initialize the media and character devices */
>  	cdev_init(&devnode->cdev, &media_devnode_fops);
>  	devnode->cdev.owner = owner;
>  	kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor);
>  
> -	ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t),
> -					     devnode->minor), 1);
> -	if (ret < 0) {
> -		pr_err("%s: cdev_add failed\n", __func__);
> -		goto error;
> -	}
> -
> -	/* Part 3: Register the media device */
>  	devnode->dev.bus = &media_bus_type;
>  	devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
>  	devnode->dev.release = media_devnode_release;
>  	if (devnode->parent)
>  		devnode->dev.parent = devnode->parent;
>  	dev_set_name(&devnode->dev, "media%d", devnode->minor);
> -	ret = device_register(&devnode->dev);
> +	device_initialize(&devnode->dev);
> +
> +	/* Part 3: Add the media and character devices */
> +	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
>  	if (ret < 0) {
> -		pr_err("%s: device_register failed\n", __func__);
> -		goto error;
> +		pr_err("%s: cdev_device_add failed\n", __func__);
> +		goto cdev_add_error;
>  	}
>  
>  	/* Part 4: Activate this minor. The char device can now be used. */
> @@ -262,9 +257,9 @@ int __must_check media_devnode_register(struct media_device *mdev,
>  
>  	return 0;
>  
> -error:
> +cdev_add_error:
>  	mutex_lock(&media_devnode_lock);
> -	cdev_del(&devnode->cdev);
> +	cdev_device_del(&devnode->cdev, &devnode->dev);
>  	clear_bit(devnode->minor, media_devnode_nums);
>  	mutex_unlock(&media_devnode_lock);
>  

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 04/29] media: mc: utilize new cdev_device_add helper function
  2024-02-07  9:38   ` Laurent Pinchart
@ 2024-02-07  9:51     ` Laurent Pinchart
  2024-02-21 12:55       ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07  9:51 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

On Wed, Feb 07, 2024 at 11:38:18AM +0200, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.
> 
> On Wed, Dec 20, 2023 at 12:36:48PM +0200, Sakari Ailus wrote:
> > From: Logan Gunthorpe <logang@deltatee.com>
> > 
> > Replace the open coded registration of the cdev and dev with the
> > new device_add_cdev() helper. The helper replaces a common pattern by
> > taking the proper reference against the parent device and adding both
> > the cdev and the device.
> > 
> > Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
> > Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
> > Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> 
> This reapplies a commit you've reverted in 02/29 in this series. I
> understand this is done to be able to apply the revert in 03/29 cleanly.
> Given that those three patches are consecutive, wouldn't it be better to
> squash 02/29, 03/29 and 04/29, with the commit message of 03/29 ?
> Otherwise, I would at least drop the Acked-by and Reviewed-by tags in
> the patches you reapply, as they've been reviewed in a different
> context.
> 
> The same applies to patches 05/29, 06/29 and 07/29.

And especially to those patches actually. 06/29 has a single line change
for the uvcvideo driver, the revert in 05/29 and re-revert in 07/29 seem
overkill.

It would also be nice to expand the commit messages of 03/29 and 06/29
to explain why the revert are needed.

> > ---
> >  drivers/media/cec/core/cec-core.c | 16 ++++------------
> >  drivers/media/mc/mc-devnode.c     | 23 +++++++++--------------
> >  2 files changed, 13 insertions(+), 26 deletions(-)
> > 
> > diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
> > index 0645e68411fb..15494b46458a 100644
> > --- a/drivers/media/cec/core/cec-core.c
> > +++ b/drivers/media/cec/core/cec-core.c
> > @@ -137,26 +137,19 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode,
> >  
> >  	/* Part 2: Initialize and register the character device */
> >  	cdev_init(&devnode->cdev, &cec_devnode_fops);
> > -	devnode->cdev.kobj.parent = &devnode->dev.kobj;
> >  	devnode->cdev.owner = owner;
> >  	kobject_set_name(&devnode->cdev.kobj, "cec%d", devnode->minor);
> >  
> >  	devnode->registered = true;
> > -	ret = cdev_add(&devnode->cdev, devnode->dev.devt, 1);
> > -	if (ret < 0) {
> > -		pr_err("%s: cdev_add failed\n", __func__);
> > +	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
> > +	if (ret) {
> > +		pr_err("%s: cdev_device_add failed\n", __func__);
> >  		devnode->registered = false;
> >  		goto clr_bit;
> >  	}
> >  
> > -	ret = device_add(&devnode->dev);
> > -	if (ret)
> > -		goto cdev_del;
> > -
> >  	return 0;
> >  
> > -cdev_del:
> > -	cdev_del(&devnode->cdev);
> >  clr_bit:
> >  	mutex_lock(&cec_devnode_lock);
> >  	clear_bit(devnode->minor, cec_devnode_nums);
> > @@ -202,8 +195,7 @@ static void cec_devnode_unregister(struct cec_adapter *adap)
> >  	cec_adap_enable(adap);
> >  	mutex_unlock(&adap->lock);
> >  
> > -	device_del(&devnode->dev);
> > -	cdev_del(&devnode->cdev);
> > +	cdev_device_del(&devnode->cdev, &devnode->dev);
> >  	put_device(&devnode->dev);
> >  }
> >  
> > diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> > index 1e1792c3ae3f..fabcd646679b 100644
> > --- a/drivers/media/mc/mc-devnode.c
> > +++ b/drivers/media/mc/mc-devnode.c
> > @@ -232,29 +232,24 @@ int __must_check media_devnode_register(struct media_device *mdev,
> >  	devnode->minor = minor;
> >  	devnode->media_dev = mdev;
> >  
> > -	/* Part 2: Initialize and register the character device */
> > +	/* Part 2: Initialize the media and character devices */
> >  	cdev_init(&devnode->cdev, &media_devnode_fops);
> >  	devnode->cdev.owner = owner;
> >  	kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor);
> >  
> > -	ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t),
> > -					     devnode->minor), 1);
> > -	if (ret < 0) {
> > -		pr_err("%s: cdev_add failed\n", __func__);
> > -		goto error;
> > -	}
> > -
> > -	/* Part 3: Register the media device */
> >  	devnode->dev.bus = &media_bus_type;
> >  	devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
> >  	devnode->dev.release = media_devnode_release;
> >  	if (devnode->parent)
> >  		devnode->dev.parent = devnode->parent;
> >  	dev_set_name(&devnode->dev, "media%d", devnode->minor);
> > -	ret = device_register(&devnode->dev);
> > +	device_initialize(&devnode->dev);
> > +
> > +	/* Part 3: Add the media and character devices */
> > +	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
> >  	if (ret < 0) {
> > -		pr_err("%s: device_register failed\n", __func__);
> > -		goto error;
> > +		pr_err("%s: cdev_device_add failed\n", __func__);
> > +		goto cdev_add_error;
> >  	}
> >  
> >  	/* Part 4: Activate this minor. The char device can now be used. */
> > @@ -262,9 +257,9 @@ int __must_check media_devnode_register(struct media_device *mdev,
> >  
> >  	return 0;
> >  
> > -error:
> > +cdev_add_error:
> >  	mutex_lock(&media_devnode_lock);
> > -	cdev_del(&devnode->cdev);
> > +	cdev_device_del(&devnode->cdev, &devnode->dev);
> >  	clear_bit(devnode->minor, media_devnode_nums);
> >  	mutex_unlock(&media_devnode_lock);
> >  

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 08/29] media: mc: Drop nop release callback
  2023-12-20 10:36 ` [PATCH v2 08/29] media: mc: Drop nop release callback Sakari Ailus
@ 2024-02-07  9:55   ` Laurent Pinchart
  0 siblings, 0 replies; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07  9:55 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:36:52PM +0200, Sakari Ailus wrote:
> The release callback is only used to print a debug message. Drop it. (It
> will be re-introduced later in a different form.)
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> ---
>  drivers/media/mc/mc-device.c | 6 ------
>  1 file changed, 6 deletions(-)
> 
> diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
> index d4553a3705f5..c0ea08a8fc31 100644
> --- a/drivers/media/mc/mc-device.c
> +++ b/drivers/media/mc/mc-device.c
> @@ -566,11 +566,6 @@ static DEVICE_ATTR_RO(model);
>   * Registration/unregistration
>   */
>  
> -static void media_device_release(struct media_devnode *devnode)
> -{
> -	dev_dbg(devnode->parent, "Media device released\n");
> -}
> -
>  static void __media_device_unregister_entity(struct media_entity *entity)
>  {
>  	struct media_device *mdev = entity->graph_obj.mdev;
> @@ -718,7 +713,6 @@ int __must_check __media_device_register(struct media_device *mdev,
>  	/* Register the device node. */
>  	mdev->devnode.fops = &media_device_fops;
>  	mdev->devnode.parent = mdev->dev;
> -	mdev->devnode.release = media_device_release;
>  
>  	/* Set version 0 to indicate user-space that the graph is static */
>  	mdev->topology_version = 0;

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 09/29] media: mc: Do not call cdev_device_del() if cdev_device_add() fails
  2023-12-20 10:36 ` [PATCH v2 09/29] media: mc: Do not call cdev_device_del() if cdev_device_add() fails Sakari Ailus
@ 2024-02-07  9:57   ` Laurent Pinchart
  2024-03-05  8:13     ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07  9:57 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:36:53PM +0200, Sakari Ailus wrote:
> cdev_device_del() is the right function to remove a device when
> cdev_device_add() succeeds. If it does not, however, put_device() needs to
> be used instead. Fix this.

Where's the put_device() call ?

> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> ---
>  drivers/media/mc/mc-devnode.c | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> index ce93ab9be676..7e22938dfd81 100644
> --- a/drivers/media/mc/mc-devnode.c
> +++ b/drivers/media/mc/mc-devnode.c
> @@ -254,7 +254,6 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
>  
>  cdev_add_error:
>  	mutex_lock(&media_devnode_lock);
> -	cdev_device_del(&devnode->cdev, &devnode->dev);
>  	clear_bit(devnode->minor, media_devnode_nums);
>  	mutex_unlock(&media_devnode_lock);
>  

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 10/29] media: mc: Delete character device early
  2023-12-20 10:36 ` [PATCH v2 10/29] media: mc: Delete character device early Sakari Ailus
@ 2024-02-07 10:08   ` Laurent Pinchart
  2024-03-05  8:52     ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 10:08 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:36:54PM +0200, Sakari Ailus wrote:
> The parent of the character device related to the media devnode is the
> media devnode. Thus the character device needs to be released before the
> media devnode's release function. Move it to unregistering of the media
> devnode, which mirrors adding the character device in conjunction with
> registering the media devnode.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> ---
>  drivers/media/mc/mc-devnode.c | 4 +---
>  1 file changed, 1 insertion(+), 3 deletions(-)
> 
> diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> index 7e22938dfd81..8bc7450ac144 100644
> --- a/drivers/media/mc/mc-devnode.c
> +++ b/drivers/media/mc/mc-devnode.c
> @@ -51,9 +51,6 @@ static void media_devnode_release(struct device *cd)
>  
>  	mutex_lock(&media_devnode_lock);
>  
> -	/* Delete the cdev on this minor as well */
> -	cdev_del(&devnode->cdev);
> -
>  	/* Mark device node number as free */
>  	clear_bit(devnode->minor, media_devnode_nums);

Should this be moved to media_devnode_unregister() too ? It can be done
in a separate patch.

>  
> @@ -270,6 +267,7 @@ void media_devnode_unregister(struct media_devnode *devnode)
>  	clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
>  	mutex_unlock(&media_devnode_lock);
>  
> +	cdev_del(&devnode->cdev);

I initially wondered if this could raise with the cdev access in
media_open(), as the media_devnode_lock is released just before calling
cdev_dev(), but my understanding is that the dev/open race is properly
handled in the cdev layer.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

I wonder if a similar change in v4l2-dev.c would be beneficial.

>  	device_unregister(&devnode->dev);
>  }
>  

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 11/29] media: mc: Split initialising and adding media devnode
  2023-12-20 10:36 ` [PATCH v2 11/29] media: mc: Split initialising and adding media devnode Sakari Ailus
@ 2024-02-07 10:46   ` Laurent Pinchart
  2024-03-05  8:59     ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 10:46 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:36:55PM +0200, Sakari Ailus wrote:
> As registering a device node of an entity belonging to a media device

Did you mean s/As registering/Registering/ ?

> will require a reference to the struct device. Taking that reference is
> only possible once the device has been initialised, which took place only

s/took/takes/

> when it was registered. Split this in two, and initialise the device when

s/was/is/

> the media device is allocated.
> 
> Don't distribute the effects of these changes yet. Add media_device_get()
> and media_device_put() first.

Don't propagate the effects of these changes to drivers yet, we want to
expose media_device refcounting with media_device_get() and
media_device_put() functions first.


I'm not sure that's exactly what you meant though.

> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> ---
>  drivers/media/mc/mc-device.c  | 18 +++++++++++++-----
>  drivers/media/mc/mc-devnode.c | 17 ++++++++++-------
>  include/media/media-devnode.h | 19 ++++++++++++++-----
>  3 files changed, 37 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
> index c0ea08a8fc31..ebf037cd5f4a 100644
> --- a/drivers/media/mc/mc-device.c
> +++ b/drivers/media/mc/mc-device.c
> @@ -717,19 +717,26 @@ int __must_check __media_device_register(struct media_device *mdev,
>  	/* Set version 0 to indicate user-space that the graph is static */
>  	mdev->topology_version = 0;
>  
> +	media_devnode_init(&mdev->devnode);
> +
>  	ret = media_devnode_register(&mdev->devnode, owner);
>  	if (ret < 0)
> -		return ret;
> +		goto out_put;
>  
>  	ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
> -	if (ret < 0) {
> -		media_devnode_unregister(&mdev->devnode);
> -		return ret;
> -	}
> +	if (ret < 0)
> +		goto out_unregister;
>  
>  	dev_dbg(mdev->dev, "Media device registered\n");
>  
>  	return 0;
> +
> +out_unregister:

I would name the labels err_unregister and err_put.

> +	media_devnode_unregister(&mdev->devnode);
> +out_put:
> +	put_device(&mdev->devnode.dev);
> +
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(__media_device_register);
>  
> @@ -803,6 +810,7 @@ void media_device_unregister(struct media_device *mdev)
>  	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
>  	dev_dbg(mdev->dev, "Media device unregistering\n");
>  	media_devnode_unregister(&mdev->devnode);
> +	put_device(&mdev->devnode.dev);
>  }
>  EXPORT_SYMBOL_GPL(media_device_unregister);
>  
> diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> index 8bc7450ac144..7b17419050fb 100644
> --- a/drivers/media/mc/mc-devnode.c
> +++ b/drivers/media/mc/mc-devnode.c
> @@ -204,6 +204,11 @@ static const struct file_operations media_devnode_fops = {
>  	.llseek = no_llseek,
>  };
>  
> +void media_devnode_init(struct media_devnode *devnode)
> +{
> +	device_initialize(&devnode->dev);
> +}
> +
>  int __must_check media_devnode_register(struct media_devnode *devnode,
>  					struct module *owner)
>  {
> @@ -235,7 +240,6 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
>  	if (devnode->parent)
>  		devnode->dev.parent = devnode->parent;
>  	dev_set_name(&devnode->dev, "media%d", devnode->minor);
> -	device_initialize(&devnode->dev);
>  
>  	/* Part 3: Add the media and character devices */
>  	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
> @@ -267,14 +271,13 @@ void media_devnode_unregister(struct media_devnode *devnode)
>  	clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
>  	mutex_unlock(&media_devnode_lock);
>  
> -	cdev_del(&devnode->cdev);
> -	device_unregister(&devnode->dev);
> +	cdev_device_del(&devnode->cdev, &devnode->dev);
>  }
>  
>  /*
>   *	Initialise media for linux
>   */
> -static int __init media_devnode_init(void)
> +static int __init media_devnode_module_init(void)
>  {
>  	int ret;
>  
> @@ -296,14 +299,14 @@ static int __init media_devnode_init(void)
>  	return 0;
>  }
>  
> -static void __exit media_devnode_exit(void)
> +static void __exit media_devnode_module_exit(void)
>  {
>  	bus_unregister(&media_bus_type);
>  	unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
>  }
>  
> -subsys_initcall(media_devnode_init);
> -module_exit(media_devnode_exit)
> +subsys_initcall(media_devnode_module_init);
> +module_exit(media_devnode_module_exit)
>  
>  MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
>  MODULE_DESCRIPTION("Device node registration for media drivers");
> diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
> index 1117d1dfd6bf..6d46c658be21 100644
> --- a/include/media/media-devnode.h
> +++ b/include/media/media-devnode.h
> @@ -90,6 +90,17 @@ struct media_devnode {
>  /* dev to media_devnode */
>  #define to_media_devnode(cd) container_of(cd, struct media_devnode, dev)
>  
> +/**
> + * media_devnode_init - initialise a media devnode
> + *
> + * @devnode: struct media_devnode we want to initialise
> + *
> + * Initialise a media devnode. Note that after initialising the media
> + * devnode is refcounted. Releasing references to it may be done using
> + * put_device().
> + */
> +void media_devnode_init(struct media_devnode *devnode);
> +
>  /**
>   * media_devnode_register - register a media device node
>   *
> @@ -100,11 +111,9 @@ struct media_devnode {
>   * with the kernel. An error is returned if no free minor number can be found,
>   * or if the registration of the device node fails.
>   *
> - * Zero is returned on success.
> - *
> - * Note that if the media_devnode_register call fails, the release() callback of
> - * the media_devnode structure is *not* called, so the caller is responsible for
> - * freeing any data.
> + * Zero is returned on success. Note that in case
> + * media_devnode_register() fails, the caller is responsible for
> + * releasing the reference to the device using put_device().
>   */
>  int __must_check media_devnode_register(struct media_devnode *devnode,
>  					struct module *owner);

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 12/29] media: mc: Shuffle functions around
  2023-12-20 10:36 ` [PATCH v2 12/29] media: mc: Shuffle functions around Sakari Ailus
@ 2024-02-07 10:47   ` Laurent Pinchart
  0 siblings, 0 replies; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 10:47 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:36:56PM +0200, Sakari Ailus wrote:
> As the call paths of the functions in question will change, move them
> around in anticipation of that. No other changes.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Acked-by: Hans Verkuil <hans.verkuil@cisco.com>

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> ---
>  drivers/media/mc/mc-device.c | 54 ++++++++++++++++++------------------
>  1 file changed, 27 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
> index ebf037cd5f4a..44685ab6a450 100644
> --- a/drivers/media/mc/mc-device.c
> +++ b/drivers/media/mc/mc-device.c
> @@ -673,6 +673,33 @@ void media_device_unregister_entity(struct media_entity *entity)
>  }
>  EXPORT_SYMBOL_GPL(media_device_unregister_entity);
>  
> +void media_device_register_entity_notify(struct media_device *mdev,
> +					struct media_entity_notify *nptr)
> +{
> +	mutex_lock(&mdev->graph_mutex);
> +	list_add_tail(&nptr->list, &mdev->entity_notify);
> +	mutex_unlock(&mdev->graph_mutex);
> +}
> +EXPORT_SYMBOL_GPL(media_device_register_entity_notify);
> +
> +/*
> + * Note: Should be called with mdev->lock held.
> + */
> +static void __media_device_unregister_entity_notify(struct media_device *mdev,
> +					struct media_entity_notify *nptr)
> +{
> +	list_del(&nptr->list);
> +}
> +
> +void media_device_unregister_entity_notify(struct media_device *mdev,
> +					struct media_entity_notify *nptr)
> +{
> +	mutex_lock(&mdev->graph_mutex);
> +	__media_device_unregister_entity_notify(mdev, nptr);
> +	mutex_unlock(&mdev->graph_mutex);
> +}
> +EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify);
> +
>  void media_device_init(struct media_device *mdev)
>  {
>  	INIT_LIST_HEAD(&mdev->entities);
> @@ -740,33 +767,6 @@ int __must_check __media_device_register(struct media_device *mdev,
>  }
>  EXPORT_SYMBOL_GPL(__media_device_register);
>  
> -void media_device_register_entity_notify(struct media_device *mdev,
> -					struct media_entity_notify *nptr)
> -{
> -	mutex_lock(&mdev->graph_mutex);
> -	list_add_tail(&nptr->list, &mdev->entity_notify);
> -	mutex_unlock(&mdev->graph_mutex);
> -}
> -EXPORT_SYMBOL_GPL(media_device_register_entity_notify);
> -
> -/*
> - * Note: Should be called with mdev->lock held.
> - */
> -static void __media_device_unregister_entity_notify(struct media_device *mdev,
> -					struct media_entity_notify *nptr)
> -{
> -	list_del(&nptr->list);
> -}
> -
> -void media_device_unregister_entity_notify(struct media_device *mdev,
> -					struct media_entity_notify *nptr)
> -{
> -	mutex_lock(&mdev->graph_mutex);
> -	__media_device_unregister_entity_notify(mdev, nptr);
> -	mutex_unlock(&mdev->graph_mutex);
> -}
> -EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify);
> -
>  void media_device_unregister(struct media_device *mdev)
>  {
>  	struct media_entity *entity;

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 13/29] media: mc: Initialise media devnode in media_device_init()
  2023-12-20 10:36 ` [PATCH v2 13/29] media: mc: Initialise media devnode in media_device_init() Sakari Ailus
@ 2024-02-07 10:51   ` Laurent Pinchart
  0 siblings, 0 replies; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 10:51 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:36:57PM +0200, Sakari Ailus wrote:
> Call media_devnode_init() from media_device_init(). This has the effect of
> creating a struct device for the media_devnode before it is registered,
> making it possible to obtain a reference to it for e.g. video devices.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> ---
>  drivers/media/mc/mc-device.c | 21 +++++++--------------
>  1 file changed, 7 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
> index 44685ab6a450..e6ac9b066524 100644
> --- a/drivers/media/mc/mc-device.c
> +++ b/drivers/media/mc/mc-device.c
> @@ -711,8 +711,8 @@ void media_device_init(struct media_device *mdev)
>  	mutex_init(&mdev->req_queue_mutex);
>  	mutex_init(&mdev->graph_mutex);
>  	ida_init(&mdev->entity_internal_idx);
> -
>  	atomic_set(&mdev->request_id, 0);

I would add a blank line here.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> +	media_devnode_init(&mdev->devnode);
>  
>  	if (!*mdev->bus_info)
>  		media_set_bus_info(mdev->bus_info, sizeof(mdev->bus_info),
> @@ -729,6 +729,7 @@ void media_device_cleanup(struct media_device *mdev)
>  	media_graph_walk_cleanup(&mdev->pm_count_walk);
>  	mutex_destroy(&mdev->graph_mutex);
>  	mutex_destroy(&mdev->req_queue_mutex);
> +	put_device(&mdev->devnode.dev);
>  }
>  EXPORT_SYMBOL_GPL(media_device_cleanup);
>  
> @@ -744,26 +745,19 @@ int __must_check __media_device_register(struct media_device *mdev,
>  	/* Set version 0 to indicate user-space that the graph is static */
>  	mdev->topology_version = 0;
>  
> -	media_devnode_init(&mdev->devnode);
> -
>  	ret = media_devnode_register(&mdev->devnode, owner);
>  	if (ret < 0)
> -		goto out_put;
> +		return ret;
>  
>  	ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
> -	if (ret < 0)
> -		goto out_unregister;
> +	if (ret < 0) {
> +		media_devnode_unregister(&mdev->devnode);
> +		return ret;
> +	}
>  
>  	dev_dbg(mdev->dev, "Media device registered\n");
>  
>  	return 0;
> -
> -out_unregister:
> -	media_devnode_unregister(&mdev->devnode);
> -out_put:
> -	put_device(&mdev->devnode.dev);
> -
> -	return ret;
>  }
>  EXPORT_SYMBOL_GPL(__media_device_register);
>  
> @@ -810,7 +804,6 @@ void media_device_unregister(struct media_device *mdev)
>  	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
>  	dev_dbg(mdev->dev, "Media device unregistering\n");
>  	media_devnode_unregister(&mdev->devnode);
> -	put_device(&mdev->devnode.dev);
>  }
>  EXPORT_SYMBOL_GPL(media_device_unregister);
>  

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 14/29] media: mc: Refactor media devnode minor clearing
  2023-12-20 10:36 ` [PATCH v2 14/29] media: mc: Refactor media devnode minor clearing Sakari Ailus
  2024-02-05 14:46   ` Hans Verkuil
@ 2024-02-07 10:53   ` Laurent Pinchart
  1 sibling, 0 replies; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 10:53 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:36:58PM +0200, Sakari Ailus wrote:
> Refactor clearing media devnode minor bit in media devnode bitmap. Note
> that number is used instead of struct media_devnode as argument since the
> minor number will also be stored in a different structure soon.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>

Reviewed-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

> ---
>  drivers/media/mc/mc-devnode.c | 19 +++++++++++--------
>  1 file changed, 11 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> index 7b17419050fb..717408791a7c 100644
> --- a/drivers/media/mc/mc-devnode.c
> +++ b/drivers/media/mc/mc-devnode.c
> @@ -44,17 +44,22 @@ static dev_t media_dev_t;
>  static DEFINE_MUTEX(media_devnode_lock);
>  static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
>  
> -/* Called when the last user of the media device exits. */
> -static void media_devnode_release(struct device *cd)
> +static void media_devnode_free_minor(unsigned int minor)
>  {
> -	struct media_devnode *devnode = to_media_devnode(cd);
> -
>  	mutex_lock(&media_devnode_lock);
>  
>  	/* Mark device node number as free */
> -	clear_bit(devnode->minor, media_devnode_nums);
> +	clear_bit(minor, media_devnode_nums);
>  
>  	mutex_unlock(&media_devnode_lock);
> +}
> +
> +/* Called when the last user of the media device exits. */
> +static void media_devnode_release(struct device *cd)
> +{
> +	struct media_devnode *devnode = to_media_devnode(cd);
> +
> +	media_devnode_free_minor(devnode->minor);
>  
>  	/* Release media_devnode and perform other cleanups as needed. */
>  	if (devnode->release)
> @@ -254,9 +259,7 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
>  	return 0;
>  
>  cdev_add_error:
> -	mutex_lock(&media_devnode_lock);
> -	clear_bit(devnode->minor, media_devnode_nums);
> -	mutex_unlock(&media_devnode_lock);
> +	media_devnode_free_minor(devnode->minor);
>  
>  	return ret;
>  }

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 00/29] Media device lifetime management
  2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
                   ` (29 preceding siblings ...)
  2023-12-20 10:52 ` [PATCH v2 00/29] Media device lifetime management Laurent Pinchart
@ 2024-02-07 10:55 ` Laurent Pinchart
  2024-03-07 10:57   ` Sakari Ailus
  30 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 10:55 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

I've just noticed that I send multiple Reviewed-by tags for this series
using Laurent Pinchart <laurent.pinchart@ideasonboard.com> when I meant
Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>. Could you
replace the e-mail address when picking up the tags ?Sorry about the
inconvenience.

On Wed, Dec 20, 2023 at 12:36:44PM +0200, Sakari Ailus wrote:
> Hi folks,
> 
> This is a refresh of my 2016 RFC patchset to start addressing object
> lifetime issues in Media controller. It further allows continuing work to
> address lifetime management of media entities.
> 
> The underlying problem is described in detail in v4 of the previous RFC:
> <URL:https://lore.kernel.org/linux-media/20161108135438.GO3217@valkosipuli.retiisi.org.uk/>.
> In brief, there is currently no connection between releasing media device
> (and related) memory and IOCTL calls, meaning that there is a time window
> during which released kernel memory can be accessed, and that access can be
> triggered from the user space. The only reason why this is not a grave
> security issue is that it is not triggerable by the user alone but requires
> unbinding a device. That is still not an excuse for not fixing it.
> 
> This set differs from the earlier RFC to address the issue in the
> following respects:
> 
> - Make changes for ipu3-cio2 driver, too.
> 
> - Continue to provide best effort attempt to keep the window between device
>   removal and user space being able to access released memory as small as
>   possible. This means the problem won't become worse for drivers for which
>   Media device lifetime management has not been implemented.
> 
> The latter is achieved by adding a new object, Media devnode compat
> reference, which is allocated, refcounted and eventually released by the
> Media controller framework itself, and where the information on registration
> and open filehandles is maintained. This is only done if the driver does not
> manage the lifetime of the media device itself, i.e. its release operation
> is NULL.
> 
> Due to this, Media device file handles will also be introduced by this
> patchset. I thought the first user of this would be Media device events but
> it seems we already need them here.
> 
> Both ipu3-cio2 and omap3isp drivers are relieved of devm_request_irq() use,
> as device_release() releases the resources before calling the driver's
> remove function. While further work will be required also on these drivers
> to safely stop he hardware at unbind time, I don't see a reason not to merge
> these patches now.
> 
> Some patches are temporarily reverted in order to make reworks easier, then
> applied later on.
> 
> I've tested this on ipu3-cio2 with and without the refcounting patch (media:
> ipu3-cio2: Release the cio2 device context by media device callback),
> including failures in a few parts of the driver initialisation process in
> the MC framework.
> 
> Questions and comments are welcome.
> 
> since v1:
> 
> - Align subject prefixes with current media tree practices.
> 
> - Make release changes to the vimc driver (last patch of the set). This
>   was actually easy as vimc already centralised resource release to struct
>   v4l2_device, so it was just moved to the media device.
> 
> - Move cdev field to struct media_devnode_compat_ref and add dev field to
>   the struct, these are needed during device release. This now includes
>   also the character device which is accessed by __fput(). I've now tested
>   ipu3-cio2 and vimc with KASAN. As a by-product the kref in struct
>   media_devnode_compat_ref becomes redundant and is removed. Both devices
>   are registered in case of best effort memory safety support and used for
>   refcounting.
> 
> - Drop omap3isp driver patch moving away from devm_request_irq().
> 
> - Add a patch to warn of drivers not releasing media device safely (i.e.
>   relying on the best effort memory safety mechanism without refcounting).
> 
> - Add a patch to document how the best effort memory release safety helper
>   works.
> 
> - Add a note on releasing driver's context with the media device, not the
>   V4L2 device, in MC documentation.
> 
> - Check media device is registered before accessing its fops in
>   media_read(), media_write(), media_ioctl and media_compat_ioctl().
> 
> - Document best effort media device lifetime management (new patch).
> 
> - Use media_devnode_free_minor() in unallocating device node minor number
>   in media_devnode_register().
> 
> - Continue to rely on devm_register_irq() in ipu3-cio2 driver but register
>   the IRQ later on (compared to v1).
> 
> - Drop the patch to move away from devm_request_irq() in omap3isp.
> 
> - Fix putting references to media device and V4L2 device in 
>   v4l2_device_release().
> 
> - Add missing media_device_get() (in v1) for M2M devices in
>   video_register_media_controller().
> 
> - Unconditionally set the media devnode release function in
>   media_device_init(). There's no harm doing so and the caller of
>   media_device_init() may set the ops after calling the function.
> 
> Daniel Axtens (1):
>   media: uvcvideo: Refactor teardown of uvc on USB disconnect
> 
> Laurent Pinchart (1):
>   media: mc: Add per-file-handle data support
> 
> Logan Gunthorpe (1):
>   media: mc: utilize new cdev_device_add helper function
> 
> Sakari Ailus (26):
>   Revert "[media] media: fix media devnode ioctl/syscall and unregister
>     race"
>   Revert "media: utilize new cdev_device_add helper function"
>   Revert "[media] media: fix use-after-free in cdev_put() when app exits
>     after driver unbind"
>   Revert "media: uvcvideo: Refactor teardown of uvc on USB disconnect"
>   Revert "[media] media-device: dynamically allocate struct
>     media_devnode"
>   media: mc: Drop nop release callback
>   media: mc: Do not call cdev_device_del() if cdev_device_add() fails
>   media: mc: Delete character device early
>   media: mc: Split initialising and adding media devnode
>   media: mc: Shuffle functions around
>   media: mc: Initialise media devnode in media_device_init()
>   media: mc: Refactor media devnode minor clearing
>   media: mc: Unassign minor only if it has been assigned
>   media: mc: Refcount the media device
>   media: v4l: Acquire a reference to the media device for every video
>     device
>   media: mc: Postpone graph object removal until free
>   media: omap3isp: Release the isp device struct by media device
>     callback
>   media: ipu3-cio2: Call v4l2_device_unregister() earlier
>   media: ipu3-cio2: Request IRQ earlier
>   media: ipu3-cio2: Release the cio2 device context by media device
>     callback
>   media: vimc: Release resources on media device release
>   media: Documentation: Document how Media device resources are released
>   media: mc: Maintain a list of open file handles in a media device
>   media: mc: Implement best effort media device removal safety sans
>     refcount
>   media: mc: Warn about drivers not releasing media device safely
>   media: Documentation: Document media device memory safety helper
> 
>  Documentation/driver-api/media/mc-core.rst  |  18 +-
>  drivers/media/cec/core/cec-core.c           |   2 +-
>  drivers/media/mc/mc-device.c                | 260 ++++++++++++--------
>  drivers/media/mc/mc-devnode.c               | 230 +++++++++++------
>  drivers/media/pci/intel/ipu3/ipu3-cio2.c    |  70 ++++--
>  drivers/media/platform/ti/omap3isp/isp.c    |  24 +-
>  drivers/media/test-drivers/vimc/vimc-core.c |  15 +-
>  drivers/media/usb/au0828/au0828-core.c      |   4 +-
>  drivers/media/usb/uvc/uvc_driver.c          |   2 +-
>  drivers/media/v4l2-core/v4l2-dev.c          |  65 +++--
>  drivers/staging/media/sunxi/cedrus/cedrus.c |   2 +-
>  include/media/media-device.h                |  46 +++-
>  include/media/media-devnode.h               | 136 +++++++---
>  include/media/media-fh.h                    |  32 +++
>  14 files changed, 632 insertions(+), 274 deletions(-)
>  create mode 100644 include/media/media-fh.h

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 15/29] media: mc: Unassign minor only if it has been assigned
  2023-12-20 10:36 ` [PATCH v2 15/29] media: mc: Unassign minor only if it has been assigned Sakari Ailus
  2024-02-05 14:48   ` Hans Verkuil
@ 2024-02-07 10:58   ` Laurent Pinchart
  2024-02-21  9:24     ` Sakari Ailus
  1 sibling, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 10:58 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:36:59PM +0200, Sakari Ailus wrote:
> Assign the media device minor to -1 if it has not been previously
> assigned. There's no dependence to this yes but it will be required by
> patch "media: mc: Implement best effort media device removal safety sans
> refcount" soon.

After a quick look at that patch, I don't see the dependency I'm afraid.
Could you please explain it better ?

> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> ---
>  drivers/media/mc/mc-devnode.c | 9 ++++++++-
>  include/media/media-devnode.h | 2 +-
>  2 files changed, 9 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> index 717408791a7c..5057c48f8870 100644
> --- a/drivers/media/mc/mc-devnode.c
> +++ b/drivers/media/mc/mc-devnode.c
> @@ -59,7 +59,8 @@ static void media_devnode_release(struct device *cd)
>  {
>  	struct media_devnode *devnode = to_media_devnode(cd);
>  
> -	media_devnode_free_minor(devnode->minor);
> +	if (devnode->minor != -1)
> +		media_devnode_free_minor(devnode->minor);

Should the test be moved to media_devnode_free_minor() ?

>  
>  	/* Release media_devnode and perform other cleanups as needed. */
>  	if (devnode->release)
> @@ -212,6 +213,7 @@ static const struct file_operations media_devnode_fops = {
>  void media_devnode_init(struct media_devnode *devnode)
>  {
>  	device_initialize(&devnode->dev);
> +	devnode->minor = -1;
>  }
>  
>  int __must_check media_devnode_register(struct media_devnode *devnode,
> @@ -220,6 +222,9 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
>  	int minor;
>  	int ret;
>  
> +	if (devnode->minor != -1)
> +		return -EINVAL;
> +
>  	/* Part 1: Find a free minor number */
>  	mutex_lock(&media_devnode_lock);
>  	minor = find_first_zero_bit(media_devnode_nums, MEDIA_NUM_DEVICES);
> @@ -261,6 +266,8 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
>  cdev_add_error:
>  	media_devnode_free_minor(devnode->minor);
>  
> +	devnode->minor = -1;
> +
>  	return ret;
>  }
>  
> diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
> index 6d46c658be21..d050f54f252a 100644
> --- a/include/media/media-devnode.h
> +++ b/include/media/media-devnode.h
> @@ -60,7 +60,7 @@ struct media_file_operations {
>   * @dev:	pointer to struct &device containing the media controller device
>   * @cdev:	struct cdev pointer character device
>   * @parent:	parent device
> - * @minor:	device node minor number
> + * @minor:	device node minor number, -1 if unassigned
>   * @flags:	flags, combination of the ``MEDIA_FLAG_*`` constants
>   * @release:	release callback called at the end of ``media_devnode_release()``
>   *		routine at media-device.c.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 16/29] media: mc: Refcount the media device
  2023-12-20 10:37 ` [PATCH v2 16/29] media: mc: Refcount the media device Sakari Ailus
@ 2024-02-07 11:08   ` Laurent Pinchart
  2024-03-07 10:37     ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 11:08 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:37:00PM +0200, Sakari Ailus wrote:
> As the struct media_device embeds struct media_devnode, the lifetime of
> that object must be that same than that of the media_device.
> 
> References are obtained by media_device_get() and released by
> media_device_put(). In order to use refcounting, the driver must set the
> release callback before calling media_device_init() on the media device.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> ---
>  drivers/media/mc/mc-device.c  | 36 +++++++++++++++++++++++++++++------
>  drivers/media/mc/mc-devnode.c |  6 +++++-
>  include/media/media-device.h  | 28 +++++++++++++++++++++++++++
>  3 files changed, 63 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
> index e6ac9b066524..bbc233e726d2 100644
> --- a/drivers/media/mc/mc-device.c
> +++ b/drivers/media/mc/mc-device.c
> @@ -700,6 +700,31 @@ void media_device_unregister_entity_notify(struct media_device *mdev,
>  }
>  EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify);
>  
> +static void __media_device_release(struct media_device *mdev)
> +{
> +	dev_dbg(mdev->dev, "Media device released\n");
> +
> +	ida_destroy(&mdev->entity_internal_idx);
> +	mdev->entity_internal_idx_max = 0;
> +	media_graph_walk_cleanup(&mdev->pm_count_walk);
> +	mutex_destroy(&mdev->graph_mutex);
> +	mutex_destroy(&mdev->req_queue_mutex);
> +}
> +
> +static void media_device_release(struct media_devnode *devnode)
> +{
> +	struct media_device *mdev = to_media_device(devnode);
> +
> +	if (mdev->ops && mdev->ops->release) {
> +		/*
> +		 * If release op isn't set, __media_device_release() is called
> +		 * via media_device_cleanup().
> +		 */
> +		__media_device_release(mdev);
> +		mdev->ops->release(mdev);
> +	}
> +}
> +
>  void media_device_init(struct media_device *mdev)
>  {
>  	INIT_LIST_HEAD(&mdev->entities);
> @@ -712,6 +737,8 @@ void media_device_init(struct media_device *mdev)
>  	mutex_init(&mdev->graph_mutex);
>  	ida_init(&mdev->entity_internal_idx);
>  	atomic_set(&mdev->request_id, 0);
> +
> +	mdev->devnode.release = media_device_release;
>  	media_devnode_init(&mdev->devnode);
>  
>  	if (!*mdev->bus_info)
> @@ -724,12 +751,9 @@ EXPORT_SYMBOL_GPL(media_device_init);
>  
>  void media_device_cleanup(struct media_device *mdev)
>  {
> -	ida_destroy(&mdev->entity_internal_idx);
> -	mdev->entity_internal_idx_max = 0;
> -	media_graph_walk_cleanup(&mdev->pm_count_walk);
> -	mutex_destroy(&mdev->graph_mutex);
> -	mutex_destroy(&mdev->req_queue_mutex);
> -	put_device(&mdev->devnode.dev);
> +	WARN_ON(mdev->ops && mdev->ops->release);
> +	__media_device_release(mdev);
> +	media_device_put(mdev);
>  }
>  EXPORT_SYMBOL_GPL(media_device_cleanup);
>  
> diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> index 5057c48f8870..4ea05e42dafb 100644
> --- a/drivers/media/mc/mc-devnode.c
> +++ b/drivers/media/mc/mc-devnode.c
> @@ -59,6 +59,10 @@ static void media_devnode_release(struct device *cd)
>  {
>  	struct media_devnode *devnode = to_media_devnode(cd);
>  
> +	/* If the devnode has a ref, it is simply released by the user. */
> +	if (devnode->ref)

The structure has no ref member.

> +		return;
> +
>  	if (devnode->minor != -1)
>  		media_devnode_free_minor(devnode->minor);
>  
> @@ -213,6 +217,7 @@ static const struct file_operations media_devnode_fops = {
>  void media_devnode_init(struct media_devnode *devnode)
>  {
>  	device_initialize(&devnode->dev);
> +	devnode->dev.release = media_devnode_release;
>  	devnode->minor = -1;
>  }
>  
> @@ -246,7 +251,6 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
>  
>  	devnode->dev.bus = &media_bus_type;
>  	devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
> -	devnode->dev.release = media_devnode_release;
>  	if (devnode->parent)
>  		devnode->dev.parent = devnode->parent;
>  	dev_set_name(&devnode->dev, "media%d", devnode->minor);
> diff --git a/include/media/media-device.h b/include/media/media-device.h
> index fb0855b217ce..c6816be0eee8 100644
> --- a/include/media/media-device.h
> +++ b/include/media/media-device.h
> @@ -62,6 +62,7 @@ struct media_entity_notify {
>   *	       request (and thus the buffer) must be available to the driver.
>   *	       And once a buffer is queued, then the driver can complete
>   *	       or delete objects from the request before req_queue exits.
> + * @release: Release the resources of the media device.
>   */
>  struct media_device_ops {
>  	int (*link_notify)(struct media_link *link, u32 flags,
> @@ -70,6 +71,7 @@ struct media_device_ops {
>  	void (*req_free)(struct media_request *req);
>  	int (*req_validate)(struct media_request *req);
>  	void (*req_queue)(struct media_request *req);
> +	void (*release)(struct media_device *mdev);
>  };
>  
>  /**
> @@ -219,6 +221,30 @@ struct usb_device;
>   */
>  void media_device_init(struct media_device *mdev);
>  
> +/**
> + * media_device_get() - Get a reference to a media device

Maybe mimick the get_device() wording and state "atomically increment
the reference count for the media device" ? Same for put.

> + *
> + * @mdev: media device

This should return a pointer to the media_device, as other get functions
do.

> + */
> +#define media_device_get(mdev)						\
> +	do {								\
> +		dev_dbg((mdev)->dev, "%s: get media device %s\n",	\
> +			__func__, (mdev)->bus_info);			\

Do we really need this ? I'd prefer inline functions to ensure type
safety. If we need to track the get/put callers, I think using ftrace
would be a better option.

> +		get_device(&(mdev)->devnode.dev);			\
> +	} while (0)
> +
> +/**
> + * media_device_put() - Put a reference to a media device
> + *
> + * @mdev: media device
> + */
> +#define media_device_put(mdev)						\
> +	do {								\
> +		dev_dbg((mdev)->dev, "%s: put media device %s\n",	\
> +			__func__, (mdev)->bus_info);			\
> +		put_device(&(mdev)->devnode.dev);			\
> +	} while (0)
> +
>  /**
>   * media_device_cleanup() - Cleanups a media device element
>   *
> @@ -432,6 +458,8 @@ void __media_device_usb_init(struct media_device *mdev,
>  			     const char *driver_name);
>  
>  #else
> +#define media_device_get(mdev) do { } while (0)
> +#define media_device_put(mdev) do { } while (0)
>  static inline int media_device_register(struct media_device *mdev)
>  {
>  	return 0;

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device
  2024-02-05 14:56   ` Hans Verkuil
@ 2024-02-07 11:13     ` Laurent Pinchart
  2024-02-21 10:43       ` Sakari Ailus
  2024-02-21 10:40     ` Sakari Ailus
  1 sibling, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 11:13 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Sakari Ailus, linux-media

On Mon, Feb 05, 2024 at 03:56:22PM +0100, Hans Verkuil wrote:
> On 20/12/2023 11:37, Sakari Ailus wrote:
> > The video device depends on the existence of its media device --- if there
> > is one. Acquire a reference to it.
> > 
> > Note that when the media device release callback is used, then the V4L2
> > device release callback is ignored and a warning is issued if both are
> > set.

Why is that ? The two are distinct objects, why can't they both have a
release function ?

> > 
> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > ---
> >  drivers/media/v4l2-core/v4l2-dev.c | 51 ++++++++++++++++++++----------
> >  1 file changed, 34 insertions(+), 17 deletions(-)
> > 
> > diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> > index d13954bd31fd..c1e4995eaf5c 100644
> > --- a/drivers/media/v4l2-core/v4l2-dev.c
> > +++ b/drivers/media/v4l2-core/v4l2-dev.c
> > @@ -176,6 +176,11 @@ static void v4l2_device_release(struct device *cd)
> >  {
> >  	struct video_device *vdev = to_video_device(cd);
> >  	struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
> > +	bool v4l2_dev_has_release = v4l2_dev->release;
> > +#ifdef CONFIG_MEDIA_CONTROLLER
> > +	struct media_device *mdev = v4l2_dev->mdev;
> > +	bool mdev_has_release = mdev && mdev->ops && mdev->ops->release;
> > +#endif
> >  
> >  	mutex_lock(&videodev_lock);
> >  	if (WARN_ON(video_devices[vdev->minor] != vdev)) {
> > @@ -198,8 +203,8 @@ static void v4l2_device_release(struct device *cd)
> >  
> >  	mutex_unlock(&videodev_lock);
> >  
> > -#if defined(CONFIG_MEDIA_CONTROLLER)
> > -	if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
> > +#ifdef CONFIG_MEDIA_CONTROLLER
> > +	if (mdev && vdev->vfl_dir != VFL_DIR_M2M) {
> >  		/* Remove interfaces and interface links */
> >  		media_devnode_remove(vdev->intf_devnode);
> >  		if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
> > @@ -207,23 +212,31 @@ static void v4l2_device_release(struct device *cd)
> >  	}
> >  #endif
> >  
> > -	/* Do not call v4l2_device_put if there is no release callback set.
> > -	 * Drivers that have no v4l2_device release callback might free the
> > -	 * v4l2_dev instance in the video_device release callback below, so we
> > -	 * must perform this check here.
> > -	 *
> > -	 * TODO: In the long run all drivers that use v4l2_device should use the
> > -	 * v4l2_device release callback. This check will then be unnecessary.
> > -	 */
> > -	if (v4l2_dev->release == NULL)
> > -		v4l2_dev = NULL;
> > -
> >  	/* Release video_device and perform other
> >  	   cleanups as needed. */
> >  	vdev->release(vdev);
> >  
> > -	/* Decrease v4l2_device refcount */
> > -	if (v4l2_dev)
> > +#ifdef CONFIG_MEDIA_CONTROLLER
> > +	if (mdev)
> > +		media_device_put(mdev);
> > +
> > +	/*
> > +	 * Generally both struct media_device and struct v4l2_device are
> > +	 * embedded in the same driver's context struct so having a release
> > +	 * callback in both is a bug.
> > +	 */
> > +	WARN_ON(v4l2_dev_has_release && mdev_has_release);
> 
> How about:
> 
> 	if (WARN_ON(v4l2_dev_has_release && mdev_has_release))
> 		v4l2_dev_has_release = false;
> 
> > +#endif
> > +
> > +	/*
> > +	 * Decrease v4l2_device refcount, but only if the media device doesn't
> > +	 * have a release callback.
> > +	 */
> > +	if (v4l2_dev_has_release
> > +#ifdef CONFIG_MEDIA_CONTROLLER
> > +	    && !mdev_has_release
> > +#endif
> > +	    )
> 
> Then this change is no longer needed.
> 
> General question: do we have drivers today that set both release functions?
> Because that would now cause a WARN in the kernel log with this patch.
> 
> >  		v4l2_device_put(v4l2_dev);
> >  }
> >  
> > @@ -792,11 +805,14 @@ static int video_register_media_controller(struct video_device *vdev)
> >  	u32 intf_type;
> >  	int ret;
> >  
> > -	/* Memory-to-memory devices are more complex and use
> > +	/*
> > +	 * Memory-to-memory devices are more complex and use
> >  	 * their own function to register its mc entities.

If you fix the comment style as a drive-by change, you could as well
reflow it to 80 columns.

> >  	 */
> > -	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M)
> > +	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M) {
> > +		media_device_get(vdev->v4l2_dev->mdev);
> >  		return 0;
> > +	}
> >  
> >  	vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
> >  	vdev->entity.function = MEDIA_ENT_F_UNKNOWN;
> > @@ -875,6 +891,7 @@ static int video_register_media_controller(struct video_device *vdev)
> >  
> >  	/* FIXME: how to create the other interface links? */
> >  
> > +	media_device_get(vdev->v4l2_dev->mdev);
> >  #endif
> >  	return 0;
> >  }

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 18/29] media: mc: Postpone graph object removal until free
  2023-12-20 10:37 ` [PATCH v2 18/29] media: mc: Postpone graph object removal until free Sakari Ailus
@ 2024-02-07 14:18   ` Laurent Pinchart
  0 siblings, 0 replies; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 14:18 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:37:02PM +0200, Sakari Ailus wrote:
> The media device itself will be unregistered based on it being unbound and
> driver's remove callback being called. The graph objects themselves may
> still be in use; rely on the media device release callback to release
> them.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/mc/mc-device.c | 53 +++++++++++++++++-------------------
>  1 file changed, 25 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
> index bbc233e726d2..10426c2796b6 100644
> --- a/drivers/media/mc/mc-device.c
> +++ b/drivers/media/mc/mc-device.c
> @@ -702,8 +702,33 @@ EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify);
>  
>  static void __media_device_release(struct media_device *mdev)
>  {
> +	struct media_entity *entity;
> +	struct media_entity *next;
> +	struct media_interface *intf, *tmp_intf;
> +	struct media_entity_notify *notify, *nextp;
> +
>  	dev_dbg(mdev->dev, "Media device released\n");

No need for locking ? I suppose we can't reach this point if someone
else has a reference to the media device. A comment to mention it would
be nice.

>  
> +	/* Remove all entities from the media device */
> +	list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
> +		__media_device_unregister_entity(entity);

Should the __media_device_unregister_entity() function be renamed to
__media_device_remove_entity() (in a separate patch) ? Same for
__media_device_unregister_entity_notify().

> +
> +	/* Remove all entity_notify callbacks from the media device */
> +	list_for_each_entry_safe(notify, nextp, &mdev->entity_notify, list)
> +		__media_device_unregister_entity_notify(mdev, notify);
> +
> +	/* Remove all interfaces from the media device */
> +	list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces,
> +				 graph_obj.list) {
> +		/*
> +		 * Unlink the interface, but don't free it here; the
> +		 * module which created it is responsible for freeing
> +		 * it
> +		 */
> +		__media_remove_intf_links(intf);
> +		media_gobj_destroy(&intf->graph_obj);
> +	}
> +
>  	ida_destroy(&mdev->entity_internal_idx);
>  	mdev->entity_internal_idx_max = 0;
>  	media_graph_walk_cleanup(&mdev->pm_count_walk);
> @@ -787,42 +812,14 @@ EXPORT_SYMBOL_GPL(__media_device_register);
>  
>  void media_device_unregister(struct media_device *mdev)
>  {
> -	struct media_entity *entity;
> -	struct media_entity *next;
> -	struct media_interface *intf, *tmp_intf;
> -	struct media_entity_notify *notify, *nextp;
> -
>  	if (mdev == NULL)
>  		return;
>  
>  	mutex_lock(&mdev->graph_mutex);
> -
> -	/* Check if mdev was ever registered at all */
>  	if (!media_devnode_is_registered(&mdev->devnode)) {
>  		mutex_unlock(&mdev->graph_mutex);

Unless I'm mistaken we don't need to lock the graph mutext to test this,
so I think you can drop locking completely here.

>  		return;
>  	}
> -
> -	/* Remove all entities from the media device */
> -	list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
> -		__media_device_unregister_entity(entity);
> -
> -	/* Remove all entity_notify callbacks from the media device */
> -	list_for_each_entry_safe(notify, nextp, &mdev->entity_notify, list)
> -		__media_device_unregister_entity_notify(mdev, notify);
> -
> -	/* Remove all interfaces from the media device */
> -	list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces,
> -				 graph_obj.list) {
> -		/*
> -		 * Unlink the interface, but don't free it here; the
> -		 * module which created it is responsible for freeing
> -		 * it
> -		 */
> -		__media_remove_intf_links(intf);
> -		media_gobj_destroy(&intf->graph_obj);
> -	}
> -
>  	mutex_unlock(&mdev->graph_mutex);
>  
>  	device_remove_file(&mdev->devnode.dev, &dev_attr_model);

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 19/29] media: omap3isp: Release the isp device struct by media device callback
  2023-12-20 10:37 ` [PATCH v2 19/29] media: omap3isp: Release the isp device struct by media device callback Sakari Ailus
@ 2024-02-07 14:23   ` Laurent Pinchart
  0 siblings, 0 replies; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 14:23 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:37:03PM +0200, Sakari Ailus wrote:
> Use the media device release callback to release the isp device's data
> structure. This approach has the benefit of not releasing memory which may
> still be accessed through open file handles whilst the isp driver is being
> unbound.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> ---
>  drivers/media/platform/ti/omap3isp/isp.c | 24 +++++++++++++++++++-----
>  1 file changed, 19 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/media/platform/ti/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c
> index 1cda23244c7b..ef6a781d6da1 100644
> --- a/drivers/media/platform/ti/omap3isp/isp.c
> +++ b/drivers/media/platform/ti/omap3isp/isp.c
> @@ -649,8 +649,11 @@ static irqreturn_t isp_isr(int irq, void *_isp)
>  	return IRQ_HANDLED;
>  }
>  
> +static void isp_release(struct media_device *mdev);
> +

Forward declarations are not nice :-( Any hope to reorder functions to
fix this ?

>  static const struct media_device_ops isp_media_ops = {
>  	.link_notify = v4l2_pipeline_link_notify,
> +	.release = isp_release,
>  };
>  
>  /* -----------------------------------------------------------------------------
> @@ -1607,7 +1610,6 @@ static void isp_unregister_entities(struct isp_device *isp)
>  	omap3isp_stat_unregister_entities(&isp->isp_hist);
>  
>  	v4l2_device_unregister(&isp->v4l2_dev);
> -	media_device_cleanup(&isp->media_dev);
>  }
>  
>  static int isp_link_entity(
> @@ -1955,6 +1957,19 @@ static void isp_detach_iommu(struct isp_device *isp)
>  #endif
>  }
>  
> +static void isp_release(struct media_device *mdev)
> +{
> +	struct isp_device *isp =
> +		container_of(mdev, struct isp_device, media_dev);
> +
> +	isp_cleanup_modules(isp);
> +
> +	media_entity_enum_cleanup(&isp->crashed);
> +	v4l2_async_nf_cleanup(&isp->notifier);

This duplicates the call in isp_remove().

> +
> +	kfree(isp);
> +}
> +
>  static int isp_attach_iommu(struct isp_device *isp)
>  {
>  #ifdef CONFIG_ARM_DMA_USE_IOMMU
> @@ -2004,16 +2019,15 @@ static void isp_remove(struct platform_device *pdev)
>  	v4l2_async_nf_unregister(&isp->notifier);
>  	v4l2_async_nf_cleanup(&isp->notifier);
>  	isp_unregister_entities(isp);
> -	isp_cleanup_modules(isp);
> +
>  	isp_xclk_cleanup(isp);
>  
>  	__omap3isp_get(isp, false);
>  	isp_detach_iommu(isp);
>  	__omap3isp_put(isp, false);
>  
> -	media_entity_enum_cleanup(&isp->crashed);
> -
> -	kfree(isp);
> +	/* May release isp immediately */
> +	media_device_put(&isp->media_dev);
>  }
>  
>  enum isp_of_phy {

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 20/29] media: ipu3-cio2: Call v4l2_device_unregister() earlier
  2023-12-20 10:37 ` [PATCH v2 20/29] media: ipu3-cio2: Call v4l2_device_unregister() earlier Sakari Ailus
@ 2024-02-07 14:24   ` Laurent Pinchart
  2024-03-05 10:21     ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 14:24 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:37:04PM +0200, Sakari Ailus wrote:
> v4l2_device_unregister() unregisters V4L2 sub-device nodes among other
> things. Call it before releasing memory and other resources.

Please expand the commit message, it's not immediately clear why this is
needed and what the consequences are.

> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> ---
>  drivers/media/pci/intel/ipu3/ipu3-cio2.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> index 5d3b0ffd3d08..da82d09b46ab 100644
> --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> @@ -1827,11 +1827,11 @@ static void cio2_pci_remove(struct pci_dev *pci_dev)
>  	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
>  
>  	media_device_unregister(&cio2->media_dev);
> +	v4l2_device_unregister(&cio2->v4l2_dev);
>  	v4l2_async_nf_unregister(&cio2->notifier);
>  	v4l2_async_nf_cleanup(&cio2->notifier);
>  	cio2_queues_exit(cio2);
>  	cio2_fbpt_exit_dummy(cio2);
> -	v4l2_device_unregister(&cio2->v4l2_dev);
>  	media_device_cleanup(&cio2->media_dev);
>  	mutex_destroy(&cio2->lock);
>  

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 22/29] media: ipu3-cio2: Release the cio2 device context by media device callback
  2023-12-20 10:37 ` [PATCH v2 22/29] media: ipu3-cio2: Release the cio2 device context by media device callback Sakari Ailus
@ 2024-02-07 14:33   ` Laurent Pinchart
  2024-03-07 12:23     ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 14:33 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:37:06PM +0200, Sakari Ailus wrote:
> Use the media device release callback to release the cio2 device's data
> structure. This approach has the benefit of not releasing memory which may
> still be accessed through open file handles whilst the ipu3-cio2 driver is
> being unbound.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> ---
>  drivers/media/pci/intel/ipu3/ipu3-cio2.c | 58 ++++++++++++++++--------
>  1 file changed, 40 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> index 3222ec5b8345..bff66e6d3b1e 100644
> --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> @@ -238,9 +238,15 @@ static int cio2_fbpt_init(struct cio2_device *cio2, struct cio2_queue *q)
>  	return 0;
>  }
>  
> -static void cio2_fbpt_exit(struct cio2_queue *q, struct device *dev)
> +static int cio2_fbpt_exit(struct cio2_queue *q, struct device *dev)
>  {
> +	if (!q->fbpt)
> +		return -ENOENT;
> +
>  	dma_free_coherent(dev, CIO2_FBPT_SIZE, q->fbpt, q->fbpt_bus_addr);
> +	q->fbpt = NULL;
> +
> +	return 0;
>  }
>  
>  /**************** CSI2 hardware setup ****************/
> @@ -1643,13 +1649,13 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q)
>  
>  static void cio2_queue_exit(struct cio2_device *cio2, struct cio2_queue *q)
>  {
> -	vb2_video_unregister_device(&q->vdev);
>  	media_entity_cleanup(&q->vdev.entity);
>  	v4l2_device_unregister_subdev(&q->subdev);

Is the release callback the right time for this ?

>  	media_entity_cleanup(&q->subdev.entity);
> -	cio2_fbpt_exit(q, &cio2->pci_dev->dev);
> -	mutex_destroy(&q->subdev_lock);
> -	mutex_destroy(&q->lock);
> +	if (!cio2_fbpt_exit(q, &cio2->pci_dev->dev)) {

This doesn't look very nice, but I suppose there are many other things
to clean up in this driver, so I'll close my eyes.

> +		mutex_destroy(&q->subdev_lock);
> +		mutex_destroy(&q->lock);
> +	}
>  }
>  
>  static int cio2_queues_init(struct cio2_device *cio2)
> @@ -1695,6 +1701,23 @@ static int cio2_check_fwnode_graph(struct fwnode_handle *fwnode)
>  	return cio2_check_fwnode_graph(fwnode->secondary);
>  }
>  
> +static void cio2_media_release(struct media_device *mdev)
> +{
> +	struct cio2_device *cio2 =
> +		container_of(mdev, struct cio2_device, media_dev);
> +
> +	v4l2_async_nf_cleanup(&cio2->notifier);
> +	cio2_queues_exit(cio2);
> +	cio2_fbpt_exit_dummy(cio2);
> +	mutex_destroy(&cio2->lock);
> +
> +	kfree(cio2);
> +}
> +
> +static const struct media_device_ops cio2_mdev_ops = {
> +	.release = cio2_media_release,
> +};
> +
>  /**************** PCI interface ****************/
>  
>  static int cio2_pci_probe(struct pci_dev *pci_dev,
> @@ -1722,7 +1745,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
>  			return r;
>  	}
>  
> -	cio2 = devm_kzalloc(dev, sizeof(*cio2), GFP_KERNEL);
> +	cio2 = kzalloc(sizeof(*cio2), GFP_KERNEL);
>  	if (!cio2)
>  		return -ENOMEM;
>  	cio2->pci_dev = pci_dev;
> @@ -1767,6 +1790,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
>  	mutex_init(&cio2->lock);
>  
>  	cio2->media_dev.dev = dev;
> +	cio2->media_dev.ops = &cio2_mdev_ops;
>  	strscpy(cio2->media_dev.model, CIO2_DEVICE_NAME,
>  		sizeof(cio2->media_dev.model));
>  	cio2->media_dev.hw_revision = 0;
> @@ -1774,7 +1798,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
>  	media_device_init(&cio2->media_dev);
>  	r = media_device_register(&cio2->media_dev);
>  	if (r < 0)
> -		goto fail_mutex_destroy;
> +		goto fail_media_device_put;
>  
>  	cio2->v4l2_dev.mdev = &cio2->media_dev;
>  	r = v4l2_device_register(dev, &cio2->v4l2_dev);
> @@ -1808,35 +1832,33 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
>  
>  fail_clean_notifier:
>  	v4l2_async_nf_unregister(&cio2->notifier);
> -	v4l2_async_nf_cleanup(&cio2->notifier);
> -	cio2_queues_exit(cio2);
> +
>  fail_v4l2_device_unregister:
>  	v4l2_device_unregister(&cio2->v4l2_dev);
> +
>  fail_media_device_unregister:
>  	media_device_unregister(&cio2->media_dev);
> -	media_device_cleanup(&cio2->media_dev);
> -fail_mutex_destroy:
> -	mutex_destroy(&cio2->lock);
> -	cio2_fbpt_exit_dummy(cio2);
>  
> +fail_media_device_put:
> +	media_device_put(&cio2->media_dev);
>  	return r;
>  }
>  
>  static void cio2_pci_remove(struct pci_dev *pci_dev)
>  {
>  	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
> +	unsigned int i;
>  
>  	media_device_unregister(&cio2->media_dev);
> +	for (i = 0; i < CIO2_QUEUES; i++)
> +		vb2_video_unregister_device(&cio2->queue[i].vdev);
>  	v4l2_device_unregister(&cio2->v4l2_dev);
>  	v4l2_async_nf_unregister(&cio2->notifier);
> -	v4l2_async_nf_cleanup(&cio2->notifier);
> -	cio2_queues_exit(cio2);
> -	cio2_fbpt_exit_dummy(cio2);
> -	media_device_cleanup(&cio2->media_dev);
> -	mutex_destroy(&cio2->lock);
>  
>  	pm_runtime_forbid(&pci_dev->dev);
>  	pm_runtime_get_noresume(&pci_dev->dev);
> +
> +	media_device_put(&cio2->media_dev);
>  }
>  
>  static int __maybe_unused cio2_runtime_suspend(struct device *dev)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 21/29] media: ipu3-cio2: Request IRQ earlier
  2024-02-05 14:58   ` Hans Verkuil
@ 2024-02-07 14:34     ` Laurent Pinchart
  2024-02-21 10:51       ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 14:34 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Sakari Ailus, linux-media

On Mon, Feb 05, 2024 at 03:58:45PM +0100, Hans Verkuil wrote:
> On 20/12/2023 11:37, Sakari Ailus wrote:
> > Call devm_request_irq() before registering the async notifier, as otherwise
> > it would be possible to use the device before the interrupts could be
> > deliveted to the driver.
> 
> deliveted -> delivered
> 
> Isn't this a regular fix? Ditto for the previous patch (20/29).
> 
> I'd just queue this up in the next PR.

Fixes: tags would then be nice.

> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > ---
> >  drivers/media/pci/intel/ipu3/ipu3-cio2.c | 10 +++++-----
> >  1 file changed, 5 insertions(+), 5 deletions(-)
> > 
> > diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> > index da82d09b46ab..3222ec5b8345 100644
> > --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> > +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> > @@ -1789,11 +1789,6 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
> >  
> >  	v4l2_async_nf_init(&cio2->notifier, &cio2->v4l2_dev);
> >  
> > -	/* Register notifier for subdevices we care */
> > -	r = cio2_parse_firmware(cio2);
> > -	if (r)
> > -		goto fail_clean_notifier;
> > -
> >  	r = devm_request_irq(dev, pci_dev->irq, cio2_irq, IRQF_SHARED,
> >  			     CIO2_NAME, cio2);
> >  	if (r) {
> > @@ -1801,6 +1796,11 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
> >  		goto fail_clean_notifier;
> >  	}
> >  
> > +	/* Register notifier for subdevices we care */
> > +	r = cio2_parse_firmware(cio2);
> > +	if (r)
> > +		goto fail_clean_notifier;
> > +
> >  	pm_runtime_put_noidle(dev);
> >  	pm_runtime_allow(dev);
> >  

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 23/29] media: vimc: Release resources on media device release
  2024-02-05 15:02   ` Hans Verkuil
@ 2024-02-07 14:38     ` Laurent Pinchart
  2024-02-21 10:55       ` Sakari Ailus
  2024-02-21 10:53     ` Sakari Ailus
  1 sibling, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 14:38 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Sakari Ailus, linux-media

On Mon, Feb 05, 2024 at 04:02:24PM +0100, Hans Verkuil wrote:
> On 20/12/2023 11:37, Sakari Ailus wrote:
> > Release all the resources when the media device is related, moving away

s/related/released/

> > form the struct v4l2_device used for that purpose.
> 
> form -> from

Please explain *why* in the commit message.

> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > ---
> >  drivers/media/test-drivers/vimc/vimc-core.c | 15 +++++++++------
> >  1 file changed, 9 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c
> > index af127476e920..3e59f8c256c7 100644
> > --- a/drivers/media/test-drivers/vimc/vimc-core.c
> > +++ b/drivers/media/test-drivers/vimc/vimc-core.c
> > @@ -264,13 +264,12 @@ static int vimc_add_subdevs(struct vimc_device *vimc)
> >  	return 0;
> >  }
> >  
> > -static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev)
> > +static void vimc_mdev_release(struct media_device *mdev)
> >  {
> >  	struct vimc_device *vimc =
> > -		container_of(v4l2_dev, struct vimc_device, v4l2_dev);
> > +		container_of_const(mdev, struct vimc_device, mdev);
> 
> Why this change?
> 
> >  
> >  	vimc_release_subdevs(vimc);
> > -	media_device_cleanup(&vimc->mdev);
> >  	kfree(vimc->ent_devs);
> >  	kfree(vimc);
> >  }
> > @@ -336,6 +335,10 @@ static int vimc_register_devices(struct vimc_device *vimc)
> >  	return ret;
> >  }
> >  
> > +static const struct media_device_ops vimc_mdev_ops = {
> > +	.release = vimc_mdev_release,
> > +};
> > +
> >  static int vimc_probe(struct platform_device *pdev)
> >  {
> >  	const struct font_desc *font = find_font("VGA8x16");
> > @@ -369,12 +372,12 @@ static int vimc_probe(struct platform_device *pdev)
> >  	snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info),
> >  		 "platform:%s", VIMC_PDEV_NAME);
> >  	vimc->mdev.dev = &pdev->dev;
> > +	vimc->mdev.ops = &vimc_mdev_ops;
> >  	media_device_init(&vimc->mdev);
> >  
> >  	ret = vimc_register_devices(vimc);
> >  	if (ret) {
> > -		media_device_cleanup(&vimc->mdev);
> > -		kfree(vimc);
> > +		media_device_put(&vimc->mdev);
> >  		return ret;
> >  	}
> >  	/*
> > @@ -382,7 +385,6 @@ static int vimc_probe(struct platform_device *pdev)
> >  	 * if the registration fails, we release directly from probe
> >  	 */
> >  
> > -	vimc->v4l2_dev.release = vimc_v4l2_dev_release;
> >  	platform_set_drvdata(pdev, vimc);
> >  	return 0;
> >  }
> > @@ -397,6 +399,7 @@ static void vimc_remove(struct platform_device *pdev)
> >  	media_device_unregister(&vimc->mdev);
> >  	v4l2_device_unregister(&vimc->v4l2_dev);
> >  	v4l2_device_put(&vimc->v4l2_dev);
> > +	media_device_put(&vimc->mdev);
> >  }
> >  
> >  static void vimc_dev_release(struct device *dev)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 24/29] media: Documentation: Document how Media device resources are released
  2023-12-20 10:37 ` [PATCH v2 24/29] media: Documentation: Document how Media device resources are released Sakari Ailus
  2024-02-05 15:04   ` Hans Verkuil
@ 2024-02-07 14:43   ` Laurent Pinchart
  2024-02-21 11:37     ` Sakari Ailus
  1 sibling, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-07 14:43 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Hans Verkuil

Hi Sakari,

Thank you for the patch.

On Wed, Dec 20, 2023 at 12:37:08PM +0200, Sakari Ailus wrote:
> Document that after unregistering, Media device memory resources are
> released by the release() callback rather than by calling
> media_device_cleanup().
> 
> Also add that driver memory resources should be bound to the Media device,
> not V4L2 device.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> ---
>  Documentation/driver-api/media/mc-core.rst | 18 ++++++++++++++++--
>  include/media/media-device.h               |  6 ++++--
>  2 files changed, 20 insertions(+), 4 deletions(-)
> 
> diff --git a/Documentation/driver-api/media/mc-core.rst b/Documentation/driver-api/media/mc-core.rst
> index 2456950ce8ff..346f67760671 100644
> --- a/Documentation/driver-api/media/mc-core.rst
> +++ b/Documentation/driver-api/media/mc-core.rst
> @@ -46,13 +46,27 @@ Drivers initialise media device instances by calling
>  :c:func:`media_device_init()`. After initialising a media device instance, it is
>  registered by calling :c:func:`__media_device_register()` via the macro
>  ``media_device_register()`` and unregistered by calling
> -:c:func:`media_device_unregister()`. An initialised media device must be
> -eventually cleaned up by calling :c:func:`media_device_cleanup()`.
> +:c:func:`media_device_unregister()`. The resources of an unregistered media

"of an unregistered media device" sounds weird here, I interpret it as
applying only to media devices that have never been registered.

> +device will be released by the ``release()`` callback of :c:type:`media_device`
> +ops, which will be called when the last user of the media device has released it
> +calling :c:func:`media_device_put()`.
> +
> +The ``release()`` callback is the way all the resources of the media device are
> +released once :c:func:`media_device_init()` has been called. This is also
> +relevant during device driver's probe function as the ``release()`` callback
> +will also have to be able to safely release the resources related to a partially
> +initialised media device.
>  
>  Note that it is not allowed to unregister a media device instance that was not
>  previously registered, or clean up a media device instance that was not
>  previously initialised.

Does this need an update, as we don't cleanup explicitly instead ?

>  
> +Media device and driver's per-device context
> +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> +
> +Drivers should use the struct media_device_ops ``release()`` callback to release
> +their own resources and not e.g. that of the struct v4l2_device.
> +
>  Entities
>  ^^^^^^^^
>  
> diff --git a/include/media/media-device.h b/include/media/media-device.h
> index c6816be0eee8..98e1892f1b51 100644
> --- a/include/media/media-device.h
> +++ b/include/media/media-device.h
> @@ -250,8 +250,10 @@ void media_device_init(struct media_device *mdev);
>   *
>   * @mdev:	pointer to struct &media_device
>   *
> - * This function that will destroy the graph_mutex that is
> - * initialized in media_device_init().
> + * This function that will destroy the graph_mutex that is initialized in

While at it, s/that will/will/

> + * media_device_init(). Note that *only* drivers that do not manage releasing
> + * the memory of th media device itself call this function. This function is

s/of th/of the/

"that do not manage releasing the memory of the media device itself" is
hard to understand for someone who hasn't paid close attention to the
development of this series. This text needs improvements.

> + * thus effectively DEPRECATED.
>   */
>  void media_device_cleanup(struct media_device *mdev);
>  

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 15/29] media: mc: Unassign minor only if it has been assigned
  2024-02-07 10:58   ` Laurent Pinchart
@ 2024-02-21  9:24     ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21  9:24 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil

Hi Laurent,

On Wed, Feb 07, 2024 at 12:58:15PM +0200, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.
> 
> On Wed, Dec 20, 2023 at 12:36:59PM +0200, Sakari Ailus wrote:
> > Assign the media device minor to -1 if it has not been previously
> > assigned. There's no dependence to this yes but it will be required by
> > patch "media: mc: Implement best effort media device removal safety sans
> > refcount" soon.
> 
> After a quick look at that patch, I don't see the dependency I'm afraid.
> Could you please explain it better ?
> 
> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > ---
> >  drivers/media/mc/mc-devnode.c | 9 ++++++++-
> >  include/media/media-devnode.h | 2 +-
> >  2 files changed, 9 insertions(+), 2 deletions(-)
> > 
> > diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> > index 717408791a7c..5057c48f8870 100644
> > --- a/drivers/media/mc/mc-devnode.c
> > +++ b/drivers/media/mc/mc-devnode.c
> > @@ -59,7 +59,8 @@ static void media_devnode_release(struct device *cd)
> >  {
> >  	struct media_devnode *devnode = to_media_devnode(cd);
> >  
> > -	media_devnode_free_minor(devnode->minor);
> > +	if (devnode->minor != -1)
> > +		media_devnode_free_minor(devnode->minor);
> 
> Should the test be moved to media_devnode_free_minor() ?

The intention here is to free the minor number once and only once.

Moving the test to media_devnode_free_minor() is not an option as the
devnode may be released already by that time. See the patch the commit
message refers to.

> 
> >  
> >  	/* Release media_devnode and perform other cleanups as needed. */
> >  	if (devnode->release)
> > @@ -212,6 +213,7 @@ static const struct file_operations media_devnode_fops = {
> >  void media_devnode_init(struct media_devnode *devnode)
> >  {
> >  	device_initialize(&devnode->dev);
> > +	devnode->minor = -1;
> >  }
> >  
> >  int __must_check media_devnode_register(struct media_devnode *devnode,
> > @@ -220,6 +222,9 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
> >  	int minor;
> >  	int ret;
> >  
> > +	if (devnode->minor != -1)
> > +		return -EINVAL;
> > +
> >  	/* Part 1: Find a free minor number */
> >  	mutex_lock(&media_devnode_lock);
> >  	minor = find_first_zero_bit(media_devnode_nums, MEDIA_NUM_DEVICES);
> > @@ -261,6 +266,8 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
> >  cdev_add_error:
> >  	media_devnode_free_minor(devnode->minor);
> >  
> > +	devnode->minor = -1;
> > +
> >  	return ret;
> >  }
> >  
> > diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
> > index 6d46c658be21..d050f54f252a 100644
> > --- a/include/media/media-devnode.h
> > +++ b/include/media/media-devnode.h
> > @@ -60,7 +60,7 @@ struct media_file_operations {
> >   * @dev:	pointer to struct &device containing the media controller device
> >   * @cdev:	struct cdev pointer character device
> >   * @parent:	parent device
> > - * @minor:	device node minor number
> > + * @minor:	device node minor number, -1 if unassigned
> >   * @flags:	flags, combination of the ``MEDIA_FLAG_*`` constants
> >   * @release:	release callback called at the end of ``media_devnode_release()``
> >   *		routine at media-device.c.
> 

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device
  2024-02-05 14:56   ` Hans Verkuil
  2024-02-07 11:13     ` Laurent Pinchart
@ 2024-02-21 10:40     ` Sakari Ailus
  2024-02-21 10:51       ` Hans Verkuil
  1 sibling, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21 10:40 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, laurent.pinchart

Hi Hans,

Many thanks for reviewing these.

On Mon, Feb 05, 2024 at 03:56:22PM +0100, Hans Verkuil wrote:
> On 20/12/2023 11:37, Sakari Ailus wrote:
> > The video device depends on the existence of its media device --- if there
> > is one. Acquire a reference to it.
> > 
> > Note that when the media device release callback is used, then the V4L2
> > device release callback is ignored and a warning is issued if both are
> > set.
> > 
> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > ---
> >  drivers/media/v4l2-core/v4l2-dev.c | 51 ++++++++++++++++++++----------
> >  1 file changed, 34 insertions(+), 17 deletions(-)
> > 
> > diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> > index d13954bd31fd..c1e4995eaf5c 100644
> > --- a/drivers/media/v4l2-core/v4l2-dev.c
> > +++ b/drivers/media/v4l2-core/v4l2-dev.c
> > @@ -176,6 +176,11 @@ static void v4l2_device_release(struct device *cd)
> >  {
> >  	struct video_device *vdev = to_video_device(cd);
> >  	struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
> > +	bool v4l2_dev_has_release = v4l2_dev->release;
> > +#ifdef CONFIG_MEDIA_CONTROLLER
> > +	struct media_device *mdev = v4l2_dev->mdev;
> > +	bool mdev_has_release = mdev && mdev->ops && mdev->ops->release;
> > +#endif
> >  
> >  	mutex_lock(&videodev_lock);
> >  	if (WARN_ON(video_devices[vdev->minor] != vdev)) {
> > @@ -198,8 +203,8 @@ static void v4l2_device_release(struct device *cd)
> >  
> >  	mutex_unlock(&videodev_lock);
> >  
> > -#if defined(CONFIG_MEDIA_CONTROLLER)
> > -	if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
> > +#ifdef CONFIG_MEDIA_CONTROLLER
> > +	if (mdev && vdev->vfl_dir != VFL_DIR_M2M) {
> >  		/* Remove interfaces and interface links */
> >  		media_devnode_remove(vdev->intf_devnode);
> >  		if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
> > @@ -207,23 +212,31 @@ static void v4l2_device_release(struct device *cd)
> >  	}
> >  #endif
> >  
> > -	/* Do not call v4l2_device_put if there is no release callback set.
> > -	 * Drivers that have no v4l2_device release callback might free the
> > -	 * v4l2_dev instance in the video_device release callback below, so we
> > -	 * must perform this check here.
> > -	 *
> > -	 * TODO: In the long run all drivers that use v4l2_device should use the
> > -	 * v4l2_device release callback. This check will then be unnecessary.
> > -	 */
> > -	if (v4l2_dev->release == NULL)
> > -		v4l2_dev = NULL;
> > -
> >  	/* Release video_device and perform other
> >  	   cleanups as needed. */
> >  	vdev->release(vdev);
> >  
> > -	/* Decrease v4l2_device refcount */
> > -	if (v4l2_dev)
> > +#ifdef CONFIG_MEDIA_CONTROLLER
> > +	if (mdev)
> > +		media_device_put(mdev);
> > +
> > +	/*
> > +	 * Generally both struct media_device and struct v4l2_device are
> > +	 * embedded in the same driver's context struct so having a release
> > +	 * callback in both is a bug.
> > +	 */
> > +	WARN_ON(v4l2_dev_has_release && mdev_has_release);
> 
> How about:
> 
> 	if (WARN_ON(v4l2_dev_has_release && mdev_has_release))
> 		v4l2_dev_has_release = false;
> 
> > +#endif
> > +
> > +	/*
> > +	 * Decrease v4l2_device refcount, but only if the media device doesn't
> > +	 * have a release callback.
> > +	 */
> > +	if (v4l2_dev_has_release
> > +#ifdef CONFIG_MEDIA_CONTROLLER
> > +	    && !mdev_has_release
> > +#endif
> > +	    )
> 
> Then this change is no longer needed.

Good idea.

I'll also rename v4l2_dev_has_release as v4l2_dev_call_release.

> 
> General question: do we have drivers today that set both release functions?
> Because that would now cause a WARN in the kernel log with this patch.

Indeed, the intention is to be vocal about it.

The only user of the v4l2_device release function I could find is
drivers/media/radio/dsbr100.c . I may have missed some but it certainly
isn't commonly used. Maybe we could try to drop refcounting from
v4l2_device later on?

> 
> >  		v4l2_device_put(v4l2_dev);
> >  }
> >  
> > @@ -792,11 +805,14 @@ static int video_register_media_controller(struct video_device *vdev)
> >  	u32 intf_type;
> >  	int ret;
> >  
> > -	/* Memory-to-memory devices are more complex and use
> > +	/*
> > +	 * Memory-to-memory devices are more complex and use
> >  	 * their own function to register its mc entities.
> >  	 */
> > -	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M)
> > +	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M) {
> > +		media_device_get(vdev->v4l2_dev->mdev);
> >  		return 0;
> > +	}
> >  
> >  	vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
> >  	vdev->entity.function = MEDIA_ENT_F_UNKNOWN;
> > @@ -875,6 +891,7 @@ static int video_register_media_controller(struct video_device *vdev)
> >  
> >  	/* FIXME: how to create the other interface links? */
> >  
> > +	media_device_get(vdev->v4l2_dev->mdev);
> >  #endif
> >  	return 0;
> >  }

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device
  2024-02-07 11:13     ` Laurent Pinchart
@ 2024-02-21 10:43       ` Sakari Ailus
  2024-02-21 12:19         ` Laurent Pinchart
  0 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21 10:43 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: Hans Verkuil, linux-media

Hi Laurent,

Thank you for reviewing the set!

On Wed, Feb 07, 2024 at 01:13:44PM +0200, Laurent Pinchart wrote:
> On Mon, Feb 05, 2024 at 03:56:22PM +0100, Hans Verkuil wrote:
> > On 20/12/2023 11:37, Sakari Ailus wrote:
> > > The video device depends on the existence of its media device --- if there
> > > is one. Acquire a reference to it.
> > > 
> > > Note that when the media device release callback is used, then the V4L2
> > > device release callback is ignored and a warning is issued if both are
> > > set.
> 
> Why is that ? The two are distinct objects, why can't they both have a
> release function ?

You could, in principle, but in practice both of the structs are part of
the same driver's device context struct which is a single allocation. You
can only have a single release callback for it.

> 
> > > 
> > > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > ---
> > >  drivers/media/v4l2-core/v4l2-dev.c | 51 ++++++++++++++++++++----------
> > >  1 file changed, 34 insertions(+), 17 deletions(-)
> > > 
> > > diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> > > index d13954bd31fd..c1e4995eaf5c 100644
> > > --- a/drivers/media/v4l2-core/v4l2-dev.c
> > > +++ b/drivers/media/v4l2-core/v4l2-dev.c
> > > @@ -176,6 +176,11 @@ static void v4l2_device_release(struct device *cd)
> > >  {
> > >  	struct video_device *vdev = to_video_device(cd);
> > >  	struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
> > > +	bool v4l2_dev_has_release = v4l2_dev->release;
> > > +#ifdef CONFIG_MEDIA_CONTROLLER
> > > +	struct media_device *mdev = v4l2_dev->mdev;
> > > +	bool mdev_has_release = mdev && mdev->ops && mdev->ops->release;
> > > +#endif
> > >  
> > >  	mutex_lock(&videodev_lock);
> > >  	if (WARN_ON(video_devices[vdev->minor] != vdev)) {
> > > @@ -198,8 +203,8 @@ static void v4l2_device_release(struct device *cd)
> > >  
> > >  	mutex_unlock(&videodev_lock);
> > >  
> > > -#if defined(CONFIG_MEDIA_CONTROLLER)
> > > -	if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
> > > +#ifdef CONFIG_MEDIA_CONTROLLER
> > > +	if (mdev && vdev->vfl_dir != VFL_DIR_M2M) {
> > >  		/* Remove interfaces and interface links */
> > >  		media_devnode_remove(vdev->intf_devnode);
> > >  		if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
> > > @@ -207,23 +212,31 @@ static void v4l2_device_release(struct device *cd)
> > >  	}
> > >  #endif
> > >  
> > > -	/* Do not call v4l2_device_put if there is no release callback set.
> > > -	 * Drivers that have no v4l2_device release callback might free the
> > > -	 * v4l2_dev instance in the video_device release callback below, so we
> > > -	 * must perform this check here.
> > > -	 *
> > > -	 * TODO: In the long run all drivers that use v4l2_device should use the
> > > -	 * v4l2_device release callback. This check will then be unnecessary.
> > > -	 */
> > > -	if (v4l2_dev->release == NULL)
> > > -		v4l2_dev = NULL;
> > > -
> > >  	/* Release video_device and perform other
> > >  	   cleanups as needed. */
> > >  	vdev->release(vdev);
> > >  
> > > -	/* Decrease v4l2_device refcount */
> > > -	if (v4l2_dev)
> > > +#ifdef CONFIG_MEDIA_CONTROLLER
> > > +	if (mdev)
> > > +		media_device_put(mdev);
> > > +
> > > +	/*
> > > +	 * Generally both struct media_device and struct v4l2_device are
> > > +	 * embedded in the same driver's context struct so having a release
> > > +	 * callback in both is a bug.
> > > +	 */
> > > +	WARN_ON(v4l2_dev_has_release && mdev_has_release);
> > 
> > How about:
> > 
> > 	if (WARN_ON(v4l2_dev_has_release && mdev_has_release))
> > 		v4l2_dev_has_release = false;
> > 
> > > +#endif
> > > +
> > > +	/*
> > > +	 * Decrease v4l2_device refcount, but only if the media device doesn't
> > > +	 * have a release callback.
> > > +	 */
> > > +	if (v4l2_dev_has_release
> > > +#ifdef CONFIG_MEDIA_CONTROLLER
> > > +	    && !mdev_has_release
> > > +#endif
> > > +	    )
> > 
> > Then this change is no longer needed.
> > 
> > General question: do we have drivers today that set both release functions?
> > Because that would now cause a WARN in the kernel log with this patch.
> > 
> > >  		v4l2_device_put(v4l2_dev);
> > >  }
> > >  
> > > @@ -792,11 +805,14 @@ static int video_register_media_controller(struct video_device *vdev)
> > >  	u32 intf_type;
> > >  	int ret;
> > >  
> > > -	/* Memory-to-memory devices are more complex and use
> > > +	/*
> > > +	 * Memory-to-memory devices are more complex and use
> > >  	 * their own function to register its mc entities.
> 
> If you fix the comment style as a drive-by change, you could as well
> reflow it to 80 columns.

I'll update this for the next version.

> 
> > >  	 */
> > > -	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M)
> > > +	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M) {
> > > +		media_device_get(vdev->v4l2_dev->mdev);
> > >  		return 0;
> > > +	}
> > >  
> > >  	vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
> > >  	vdev->entity.function = MEDIA_ENT_F_UNKNOWN;
> > > @@ -875,6 +891,7 @@ static int video_register_media_controller(struct video_device *vdev)
> > >  
> > >  	/* FIXME: how to create the other interface links? */
> > >  
> > > +	media_device_get(vdev->v4l2_dev->mdev);
> > >  #endif
> > >  	return 0;
> > >  }
> 

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 21/29] media: ipu3-cio2: Request IRQ earlier
  2024-02-07 14:34     ` Laurent Pinchart
@ 2024-02-21 10:51       ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21 10:51 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: Hans Verkuil, linux-media

Hi Laurent, Hans,

On Wed, Feb 07, 2024 at 04:34:18PM +0200, Laurent Pinchart wrote:
> On Mon, Feb 05, 2024 at 03:58:45PM +0100, Hans Verkuil wrote:
> > On 20/12/2023 11:37, Sakari Ailus wrote:
> > > Call devm_request_irq() before registering the async notifier, as otherwise
> > > it would be possible to use the device before the interrupts could be
> > > deliveted to the driver.
> > 
> > deliveted -> delivered
> > 
> > Isn't this a regular fix? Ditto for the previous patch (20/29).
> > 
> > I'd just queue this up in the next PR.

Yeah, I wrote it as part of the set but missed there are no further
dependencies. I'll post a new version of this separately.

> 
> Fixes: tags would then be nice.

It was in the patch adding the driver. I'll add Fixes: line.

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device
  2024-02-21 10:40     ` Sakari Ailus
@ 2024-02-21 10:51       ` Hans Verkuil
  2024-02-21 11:44         ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Hans Verkuil @ 2024-02-21 10:51 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, laurent.pinchart

On 21/02/2024 11:40, Sakari Ailus wrote:
> Hi Hans,
> 
> Many thanks for reviewing these.
> 
> On Mon, Feb 05, 2024 at 03:56:22PM +0100, Hans Verkuil wrote:
>> On 20/12/2023 11:37, Sakari Ailus wrote:
>>> The video device depends on the existence of its media device --- if there
>>> is one. Acquire a reference to it.
>>>
>>> Note that when the media device release callback is used, then the V4L2
>>> device release callback is ignored and a warning is issued if both are
>>> set.
>>>
>>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
>>> ---
>>>  drivers/media/v4l2-core/v4l2-dev.c | 51 ++++++++++++++++++++----------
>>>  1 file changed, 34 insertions(+), 17 deletions(-)
>>>
>>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
>>> index d13954bd31fd..c1e4995eaf5c 100644
>>> --- a/drivers/media/v4l2-core/v4l2-dev.c
>>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
>>> @@ -176,6 +176,11 @@ static void v4l2_device_release(struct device *cd)
>>>  {
>>>  	struct video_device *vdev = to_video_device(cd);
>>>  	struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
>>> +	bool v4l2_dev_has_release = v4l2_dev->release;
>>> +#ifdef CONFIG_MEDIA_CONTROLLER
>>> +	struct media_device *mdev = v4l2_dev->mdev;
>>> +	bool mdev_has_release = mdev && mdev->ops && mdev->ops->release;
>>> +#endif
>>>  
>>>  	mutex_lock(&videodev_lock);
>>>  	if (WARN_ON(video_devices[vdev->minor] != vdev)) {
>>> @@ -198,8 +203,8 @@ static void v4l2_device_release(struct device *cd)
>>>  
>>>  	mutex_unlock(&videodev_lock);
>>>  
>>> -#if defined(CONFIG_MEDIA_CONTROLLER)
>>> -	if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
>>> +#ifdef CONFIG_MEDIA_CONTROLLER
>>> +	if (mdev && vdev->vfl_dir != VFL_DIR_M2M) {
>>>  		/* Remove interfaces and interface links */
>>>  		media_devnode_remove(vdev->intf_devnode);
>>>  		if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
>>> @@ -207,23 +212,31 @@ static void v4l2_device_release(struct device *cd)
>>>  	}
>>>  #endif
>>>  
>>> -	/* Do not call v4l2_device_put if there is no release callback set.
>>> -	 * Drivers that have no v4l2_device release callback might free the
>>> -	 * v4l2_dev instance in the video_device release callback below, so we
>>> -	 * must perform this check here.
>>> -	 *
>>> -	 * TODO: In the long run all drivers that use v4l2_device should use the
>>> -	 * v4l2_device release callback. This check will then be unnecessary.
>>> -	 */
>>> -	if (v4l2_dev->release == NULL)
>>> -		v4l2_dev = NULL;
>>> -
>>>  	/* Release video_device and perform other
>>>  	   cleanups as needed. */
>>>  	vdev->release(vdev);
>>>  
>>> -	/* Decrease v4l2_device refcount */
>>> -	if (v4l2_dev)
>>> +#ifdef CONFIG_MEDIA_CONTROLLER
>>> +	if (mdev)
>>> +		media_device_put(mdev);
>>> +
>>> +	/*
>>> +	 * Generally both struct media_device and struct v4l2_device are
>>> +	 * embedded in the same driver's context struct so having a release
>>> +	 * callback in both is a bug.
>>> +	 */
>>> +	WARN_ON(v4l2_dev_has_release && mdev_has_release);
>>
>> How about:
>>
>> 	if (WARN_ON(v4l2_dev_has_release && mdev_has_release))
>> 		v4l2_dev_has_release = false;
>>
>>> +#endif
>>> +
>>> +	/*
>>> +	 * Decrease v4l2_device refcount, but only if the media device doesn't
>>> +	 * have a release callback.
>>> +	 */
>>> +	if (v4l2_dev_has_release
>>> +#ifdef CONFIG_MEDIA_CONTROLLER
>>> +	    && !mdev_has_release
>>> +#endif
>>> +	    )
>>
>> Then this change is no longer needed.
> 
> Good idea.
> 
> I'll also rename v4l2_dev_has_release as v4l2_dev_call_release.
> 
>>
>> General question: do we have drivers today that set both release functions?
>> Because that would now cause a WARN in the kernel log with this patch.
> 
> Indeed, the intention is to be vocal about it.
> 
> The only user of the v4l2_device release function I could find is
> drivers/media/radio/dsbr100.c . I may have missed some but it certainly
> isn't commonly used. Maybe we could try to drop refcounting from
> v4l2_device later on?

There are a lot more drivers that use this. A quick grep shows gspca, hackrf,
usbtv, pwc, au0828 and more.

git grep v4l2_dev.*release.*= drivers/media/

Currently it is the only way to properly release drivers that create multiple
video (or other) devices.

Regards,

	Hans

> 
>>
>>>  		v4l2_device_put(v4l2_dev);
>>>  }
>>>  
>>> @@ -792,11 +805,14 @@ static int video_register_media_controller(struct video_device *vdev)
>>>  	u32 intf_type;
>>>  	int ret;
>>>  
>>> -	/* Memory-to-memory devices are more complex and use
>>> +	/*
>>> +	 * Memory-to-memory devices are more complex and use
>>>  	 * their own function to register its mc entities.
>>>  	 */
>>> -	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M)
>>> +	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M) {
>>> +		media_device_get(vdev->v4l2_dev->mdev);
>>>  		return 0;
>>> +	}
>>>  
>>>  	vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
>>>  	vdev->entity.function = MEDIA_ENT_F_UNKNOWN;
>>> @@ -875,6 +891,7 @@ static int video_register_media_controller(struct video_device *vdev)
>>>  
>>>  	/* FIXME: how to create the other interface links? */
>>>  
>>> +	media_device_get(vdev->v4l2_dev->mdev);
>>>  #endif
>>>  	return 0;
>>>  }
> 


^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 23/29] media: vimc: Release resources on media device release
  2024-02-05 15:02   ` Hans Verkuil
  2024-02-07 14:38     ` Laurent Pinchart
@ 2024-02-21 10:53     ` Sakari Ailus
  2024-02-21 11:02       ` Laurent Pinchart
  2024-02-21 11:19       ` Hans Verkuil
  1 sibling, 2 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21 10:53 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, laurent.pinchart

Hi Hans,

On Mon, Feb 05, 2024 at 04:02:24PM +0100, Hans Verkuil wrote:
> On 20/12/2023 11:37, Sakari Ailus wrote:
> > Release all the resources when the media device is related, moving away

s/related/released/

> > form the struct v4l2_device used for that purpose.
> 
> form -> from

Yes.

> 
> > 
> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > ---
> >  drivers/media/test-drivers/vimc/vimc-core.c | 15 +++++++++------
> >  1 file changed, 9 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c
> > index af127476e920..3e59f8c256c7 100644
> > --- a/drivers/media/test-drivers/vimc/vimc-core.c
> > +++ b/drivers/media/test-drivers/vimc/vimc-core.c
> > @@ -264,13 +264,12 @@ static int vimc_add_subdevs(struct vimc_device *vimc)
> >  	return 0;
> >  }
> >  
> > -static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev)
> > +static void vimc_mdev_release(struct media_device *mdev)
> >  {
> >  	struct vimc_device *vimc =
> > -		container_of(v4l2_dev, struct vimc_device, v4l2_dev);
> > +		container_of_const(mdev, struct vimc_device, mdev);
> 
> Why this change?

I changed the line already. There's no reason to continue using
container_of() instead of container_of_const() that takes const-ness into
account, too.

> 
> >  
> >  	vimc_release_subdevs(vimc);
> > -	media_device_cleanup(&vimc->mdev);
> >  	kfree(vimc->ent_devs);
> >  	kfree(vimc);
> >  }
> > @@ -336,6 +335,10 @@ static int vimc_register_devices(struct vimc_device *vimc)
> >  	return ret;
> >  }
> >  
> > +static const struct media_device_ops vimc_mdev_ops = {
> > +	.release = vimc_mdev_release,
> > +};
> > +
> >  static int vimc_probe(struct platform_device *pdev)
> >  {
> >  	const struct font_desc *font = find_font("VGA8x16");
> > @@ -369,12 +372,12 @@ static int vimc_probe(struct platform_device *pdev)
> >  	snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info),
> >  		 "platform:%s", VIMC_PDEV_NAME);
> >  	vimc->mdev.dev = &pdev->dev;
> > +	vimc->mdev.ops = &vimc_mdev_ops;
> >  	media_device_init(&vimc->mdev);
> >  
> >  	ret = vimc_register_devices(vimc);
> >  	if (ret) {
> > -		media_device_cleanup(&vimc->mdev);
> > -		kfree(vimc);
> > +		media_device_put(&vimc->mdev);
> >  		return ret;
> >  	}
> >  	/*
> > @@ -382,7 +385,6 @@ static int vimc_probe(struct platform_device *pdev)
> >  	 * if the registration fails, we release directly from probe
> >  	 */
> >  
> > -	vimc->v4l2_dev.release = vimc_v4l2_dev_release;
> >  	platform_set_drvdata(pdev, vimc);
> >  	return 0;
> >  }
> > @@ -397,6 +399,7 @@ static void vimc_remove(struct platform_device *pdev)
> >  	media_device_unregister(&vimc->mdev);
> >  	v4l2_device_unregister(&vimc->v4l2_dev);
> >  	v4l2_device_put(&vimc->v4l2_dev);
> > +	media_device_put(&vimc->mdev);
> >  }
> >  
> >  static void vimc_dev_release(struct device *dev)

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 23/29] media: vimc: Release resources on media device release
  2024-02-07 14:38     ` Laurent Pinchart
@ 2024-02-21 10:55       ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21 10:55 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: Hans Verkuil, linux-media

Hi Laurent,

On Wed, Feb 07, 2024 at 04:38:05PM +0200, Laurent Pinchart wrote:
> On Mon, Feb 05, 2024 at 04:02:24PM +0100, Hans Verkuil wrote:
> > On 20/12/2023 11:37, Sakari Ailus wrote:
> > > Release all the resources when the media device is related, moving away
> 
> s/related/released/
> 
> > > form the struct v4l2_device used for that purpose.
> > 
> > form -> from
> 
> Please explain *why* in the commit message.

Just to show how the media device release callback is used. I'll add that
to the commit message.

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 23/29] media: vimc: Release resources on media device release
  2024-02-21 10:53     ` Sakari Ailus
@ 2024-02-21 11:02       ` Laurent Pinchart
  2024-02-21 11:38         ` Sakari Ailus
  2024-02-21 11:19       ` Hans Verkuil
  1 sibling, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-21 11:02 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: Hans Verkuil, linux-media

On Wed, Feb 21, 2024 at 10:53:43AM +0000, Sakari Ailus wrote:
> Hi Hans,
> 
> On Mon, Feb 05, 2024 at 04:02:24PM +0100, Hans Verkuil wrote:
> > On 20/12/2023 11:37, Sakari Ailus wrote:
> > > Release all the resources when the media device is related, moving away
> 
> s/related/released/
> 
> > > form the struct v4l2_device used for that purpose.
> > 
> > form -> from
> 
> Yes.
> 
> > 
> > > 
> > > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > ---
> > >  drivers/media/test-drivers/vimc/vimc-core.c | 15 +++++++++------
> > >  1 file changed, 9 insertions(+), 6 deletions(-)
> > > 
> > > diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c
> > > index af127476e920..3e59f8c256c7 100644
> > > --- a/drivers/media/test-drivers/vimc/vimc-core.c
> > > +++ b/drivers/media/test-drivers/vimc/vimc-core.c
> > > @@ -264,13 +264,12 @@ static int vimc_add_subdevs(struct vimc_device *vimc)
> > >  	return 0;
> > >  }
> > >  
> > > -static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev)
> > > +static void vimc_mdev_release(struct media_device *mdev)
> > >  {
> > >  	struct vimc_device *vimc =
> > > -		container_of(v4l2_dev, struct vimc_device, v4l2_dev);
> > > +		container_of_const(mdev, struct vimc_device, mdev);
> > 
> > Why this change?
> 
> I changed the line already. There's no reason to continue using
> container_of() instead of container_of_const() that takes const-ness into
> account, too.

It should then be at least mentioned in the commit message.

Any plan to switch to container_of_const() globally in the subsystem ?

> > >  
> > >  	vimc_release_subdevs(vimc);
> > > -	media_device_cleanup(&vimc->mdev);
> > >  	kfree(vimc->ent_devs);
> > >  	kfree(vimc);
> > >  }
> > > @@ -336,6 +335,10 @@ static int vimc_register_devices(struct vimc_device *vimc)
> > >  	return ret;
> > >  }
> > >  
> > > +static const struct media_device_ops vimc_mdev_ops = {
> > > +	.release = vimc_mdev_release,
> > > +};
> > > +
> > >  static int vimc_probe(struct platform_device *pdev)
> > >  {
> > >  	const struct font_desc *font = find_font("VGA8x16");
> > > @@ -369,12 +372,12 @@ static int vimc_probe(struct platform_device *pdev)
> > >  	snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info),
> > >  		 "platform:%s", VIMC_PDEV_NAME);
> > >  	vimc->mdev.dev = &pdev->dev;
> > > +	vimc->mdev.ops = &vimc_mdev_ops;
> > >  	media_device_init(&vimc->mdev);
> > >  
> > >  	ret = vimc_register_devices(vimc);
> > >  	if (ret) {
> > > -		media_device_cleanup(&vimc->mdev);
> > > -		kfree(vimc);
> > > +		media_device_put(&vimc->mdev);
> > >  		return ret;
> > >  	}
> > >  	/*
> > > @@ -382,7 +385,6 @@ static int vimc_probe(struct platform_device *pdev)
> > >  	 * if the registration fails, we release directly from probe
> > >  	 */
> > >  
> > > -	vimc->v4l2_dev.release = vimc_v4l2_dev_release;
> > >  	platform_set_drvdata(pdev, vimc);
> > >  	return 0;
> > >  }
> > > @@ -397,6 +399,7 @@ static void vimc_remove(struct platform_device *pdev)
> > >  	media_device_unregister(&vimc->mdev);
> > >  	v4l2_device_unregister(&vimc->v4l2_dev);
> > >  	v4l2_device_put(&vimc->v4l2_dev);
> > > +	media_device_put(&vimc->mdev);
> > >  }
> > >  
> > >  static void vimc_dev_release(struct device *dev)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 23/29] media: vimc: Release resources on media device release
  2024-02-21 10:53     ` Sakari Ailus
  2024-02-21 11:02       ` Laurent Pinchart
@ 2024-02-21 11:19       ` Hans Verkuil
  2024-02-21 11:40         ` Sakari Ailus
  1 sibling, 1 reply; 92+ messages in thread
From: Hans Verkuil @ 2024-02-21 11:19 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, laurent.pinchart

On 21/02/2024 11:53, Sakari Ailus wrote:
> Hi Hans,
> 
> On Mon, Feb 05, 2024 at 04:02:24PM +0100, Hans Verkuil wrote:
>> On 20/12/2023 11:37, Sakari Ailus wrote:
>>> Release all the resources when the media device is related, moving away
> 
> s/related/released/
> 
>>> form the struct v4l2_device used for that purpose.
>>
>> form -> from
> 
> Yes.
> 
>>
>>>
>>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
>>> ---
>>>  drivers/media/test-drivers/vimc/vimc-core.c | 15 +++++++++------
>>>  1 file changed, 9 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c
>>> index af127476e920..3e59f8c256c7 100644
>>> --- a/drivers/media/test-drivers/vimc/vimc-core.c
>>> +++ b/drivers/media/test-drivers/vimc/vimc-core.c
>>> @@ -264,13 +264,12 @@ static int vimc_add_subdevs(struct vimc_device *vimc)
>>>  	return 0;
>>>  }
>>>  
>>> -static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev)
>>> +static void vimc_mdev_release(struct media_device *mdev)
>>>  {
>>>  	struct vimc_device *vimc =
>>> -		container_of(v4l2_dev, struct vimc_device, v4l2_dev);
>>> +		container_of_const(mdev, struct vimc_device, mdev);
>>
>> Why this change?
> 
> I changed the line already. There's no reason to continue using
> container_of() instead of container_of_const() that takes const-ness into
> account, too.

But neither vimc nor mdev can be const anyway, that would make no sense here.

Regards,

	Hans

> 
>>
>>>  
>>>  	vimc_release_subdevs(vimc);
>>> -	media_device_cleanup(&vimc->mdev);
>>>  	kfree(vimc->ent_devs);
>>>  	kfree(vimc);
>>>  }
>>> @@ -336,6 +335,10 @@ static int vimc_register_devices(struct vimc_device *vimc)
>>>  	return ret;
>>>  }
>>>  
>>> +static const struct media_device_ops vimc_mdev_ops = {
>>> +	.release = vimc_mdev_release,
>>> +};
>>> +
>>>  static int vimc_probe(struct platform_device *pdev)
>>>  {
>>>  	const struct font_desc *font = find_font("VGA8x16");
>>> @@ -369,12 +372,12 @@ static int vimc_probe(struct platform_device *pdev)
>>>  	snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info),
>>>  		 "platform:%s", VIMC_PDEV_NAME);
>>>  	vimc->mdev.dev = &pdev->dev;
>>> +	vimc->mdev.ops = &vimc_mdev_ops;
>>>  	media_device_init(&vimc->mdev);
>>>  
>>>  	ret = vimc_register_devices(vimc);
>>>  	if (ret) {
>>> -		media_device_cleanup(&vimc->mdev);
>>> -		kfree(vimc);
>>> +		media_device_put(&vimc->mdev);
>>>  		return ret;
>>>  	}
>>>  	/*
>>> @@ -382,7 +385,6 @@ static int vimc_probe(struct platform_device *pdev)
>>>  	 * if the registration fails, we release directly from probe
>>>  	 */
>>>  
>>> -	vimc->v4l2_dev.release = vimc_v4l2_dev_release;
>>>  	platform_set_drvdata(pdev, vimc);
>>>  	return 0;
>>>  }
>>> @@ -397,6 +399,7 @@ static void vimc_remove(struct platform_device *pdev)
>>>  	media_device_unregister(&vimc->mdev);
>>>  	v4l2_device_unregister(&vimc->v4l2_dev);
>>>  	v4l2_device_put(&vimc->v4l2_dev);
>>> +	media_device_put(&vimc->mdev);
>>>  }
>>>  
>>>  static void vimc_dev_release(struct device *dev)
> 


^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 24/29] media: Documentation: Document how Media device resources are released
  2024-02-07 14:43   ` Laurent Pinchart
@ 2024-02-21 11:37     ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21 11:37 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil

Hi Laurent,

On Wed, Feb 07, 2024 at 04:43:04PM +0200, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.
> 
> On Wed, Dec 20, 2023 at 12:37:08PM +0200, Sakari Ailus wrote:
> > Document that after unregistering, Media device memory resources are
> > released by the release() callback rather than by calling
> > media_device_cleanup().
> > 
> > Also add that driver memory resources should be bound to the Media device,
> > not V4L2 device.
> > 
> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > ---
> >  Documentation/driver-api/media/mc-core.rst | 18 ++++++++++++++++--
> >  include/media/media-device.h               |  6 ++++--
> >  2 files changed, 20 insertions(+), 4 deletions(-)
> > 
> > diff --git a/Documentation/driver-api/media/mc-core.rst b/Documentation/driver-api/media/mc-core.rst
> > index 2456950ce8ff..346f67760671 100644
> > --- a/Documentation/driver-api/media/mc-core.rst
> > +++ b/Documentation/driver-api/media/mc-core.rst
> > @@ -46,13 +46,27 @@ Drivers initialise media device instances by calling
> >  :c:func:`media_device_init()`. After initialising a media device instance, it is
> >  registered by calling :c:func:`__media_device_register()` via the macro
> >  ``media_device_register()`` and unregistered by calling
> > -:c:func:`media_device_unregister()`. An initialised media device must be
> > -eventually cleaned up by calling :c:func:`media_device_cleanup()`.
> > +:c:func:`media_device_unregister()`. The resources of an unregistered media
> 
> "of an unregistered media device" sounds weird here, I interpret it as
> applying only to media devices that have never been registered.

How about "newly unregistered"?

> 
> > +device will be released by the ``release()`` callback of :c:type:`media_device`
> > +ops, which will be called when the last user of the media device has released it
> > +calling :c:func:`media_device_put()`.
> > +
> > +The ``release()`` callback is the way all the resources of the media device are
> > +released once :c:func:`media_device_init()` has been called. This is also
> > +relevant during device driver's probe function as the ``release()`` callback
> > +will also have to be able to safely release the resources related to a partially
> > +initialised media device.
> >  
> >  Note that it is not allowed to unregister a media device instance that was not
> >  previously registered, or clean up a media device instance that was not
> >  previously initialised.
> 
> Does this need an update, as we don't cleanup explicitly instead ?

I think this should remain unchanged, at least as long as there are drivers
using the old API.

> 
> >  
> > +Media device and driver's per-device context
> > +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > +
> > +Drivers should use the struct media_device_ops ``release()`` callback to release
> > +their own resources and not e.g. that of the struct v4l2_device.
> > +
> >  Entities
> >  ^^^^^^^^
> >  
> > diff --git a/include/media/media-device.h b/include/media/media-device.h
> > index c6816be0eee8..98e1892f1b51 100644
> > --- a/include/media/media-device.h
> > +++ b/include/media/media-device.h
> > @@ -250,8 +250,10 @@ void media_device_init(struct media_device *mdev);
> >   *
> >   * @mdev:	pointer to struct &media_device
> >   *
> > - * This function that will destroy the graph_mutex that is
> > - * initialized in media_device_init().
> > + * This function that will destroy the graph_mutex that is initialized in
> 
> While at it, s/that will/will/

Yes.

> 
> > + * media_device_init(). Note that *only* drivers that do not manage releasing
> > + * the memory of th media device itself call this function. This function is
> 
> s/of th/of the/
> 
> "that do not manage releasing the memory of the media device itself" is
> hard to understand for someone who hasn't paid close attention to the
> development of this series. This text needs improvements.

Hmm. How about:

Note that only drivers that do not have a proper release callback of the
struct media_device call this function.

> 
> > + * thus effectively DEPRECATED.
> >   */
> >  void media_device_cleanup(struct media_device *mdev);
> >  
> 

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 23/29] media: vimc: Release resources on media device release
  2024-02-21 11:02       ` Laurent Pinchart
@ 2024-02-21 11:38         ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21 11:38 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: Hans Verkuil, linux-media

Hi Laurent,

On Wed, Feb 21, 2024 at 01:02:22PM +0200, Laurent Pinchart wrote:
> On Wed, Feb 21, 2024 at 10:53:43AM +0000, Sakari Ailus wrote:
> > Hi Hans,
> > 
> > On Mon, Feb 05, 2024 at 04:02:24PM +0100, Hans Verkuil wrote:
> > > On 20/12/2023 11:37, Sakari Ailus wrote:
> > > > Release all the resources when the media device is related, moving away
> > 
> > s/related/released/
> > 
> > > > form the struct v4l2_device used for that purpose.
> > > 
> > > form -> from
> > 
> > Yes.
> > 
> > > 
> > > > 
> > > > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > > ---
> > > >  drivers/media/test-drivers/vimc/vimc-core.c | 15 +++++++++------
> > > >  1 file changed, 9 insertions(+), 6 deletions(-)
> > > > 
> > > > diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c
> > > > index af127476e920..3e59f8c256c7 100644
> > > > --- a/drivers/media/test-drivers/vimc/vimc-core.c
> > > > +++ b/drivers/media/test-drivers/vimc/vimc-core.c
> > > > @@ -264,13 +264,12 @@ static int vimc_add_subdevs(struct vimc_device *vimc)
> > > >  	return 0;
> > > >  }
> > > >  
> > > > -static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev)
> > > > +static void vimc_mdev_release(struct media_device *mdev)
> > > >  {
> > > >  	struct vimc_device *vimc =
> > > > -		container_of(v4l2_dev, struct vimc_device, v4l2_dev);
> > > > +		container_of_const(mdev, struct vimc_device, mdev);
> > > 
> > > Why this change?
> > 
> > I changed the line already. There's no reason to continue using
> > container_of() instead of container_of_const() that takes const-ness into
> > account, too.
> 
> It should then be at least mentioned in the commit message.

I can add that.

> 
> Any plan to switch to container_of_const() globally in the subsystem ?

This should of course be done.

I can post patches at some point unless someone gets there first. I can't
promise a quick schedule though.

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 23/29] media: vimc: Release resources on media device release
  2024-02-21 11:19       ` Hans Verkuil
@ 2024-02-21 11:40         ` Sakari Ailus
  2024-02-21 11:48           ` Hans Verkuil
  0 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21 11:40 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, laurent.pinchart

Hi Hans,

On Wed, Feb 21, 2024 at 12:19:21PM +0100, Hans Verkuil wrote:
> On 21/02/2024 11:53, Sakari Ailus wrote:
> > Hi Hans,
> > 
> > On Mon, Feb 05, 2024 at 04:02:24PM +0100, Hans Verkuil wrote:
> >> On 20/12/2023 11:37, Sakari Ailus wrote:
> >>> Release all the resources when the media device is related, moving away
> > 
> > s/related/released/
> > 
> >>> form the struct v4l2_device used for that purpose.
> >>
> >> form -> from
> > 
> > Yes.
> > 
> >>
> >>>
> >>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> >>> ---
> >>>  drivers/media/test-drivers/vimc/vimc-core.c | 15 +++++++++------
> >>>  1 file changed, 9 insertions(+), 6 deletions(-)
> >>>
> >>> diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c
> >>> index af127476e920..3e59f8c256c7 100644
> >>> --- a/drivers/media/test-drivers/vimc/vimc-core.c
> >>> +++ b/drivers/media/test-drivers/vimc/vimc-core.c
> >>> @@ -264,13 +264,12 @@ static int vimc_add_subdevs(struct vimc_device *vimc)
> >>>  	return 0;
> >>>  }
> >>>  
> >>> -static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev)
> >>> +static void vimc_mdev_release(struct media_device *mdev)
> >>>  {
> >>>  	struct vimc_device *vimc =
> >>> -		container_of(v4l2_dev, struct vimc_device, v4l2_dev);
> >>> +		container_of_const(mdev, struct vimc_device, mdev);
> >>
> >> Why this change?
> > 
> > I changed the line already. There's no reason to continue using
> > container_of() instead of container_of_const() that takes const-ness into
> > account, too.
> 
> But neither vimc nor mdev can be const anyway, that would make no sense
> here.

Neither is const, true. Yet container_of_const() is preferred over
container_of(), due to the fact that it does take const-ness into account.
container_of() should really be avoided.

I'll add this to the commit message as Laurent suggested.

-- 
Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device
  2024-02-21 10:51       ` Hans Verkuil
@ 2024-02-21 11:44         ` Sakari Ailus
  2024-03-05  7:43           ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21 11:44 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, laurent.pinchart

Hi Hans,

On Wed, Feb 21, 2024 at 11:51:08AM +0100, Hans Verkuil wrote:
> On 21/02/2024 11:40, Sakari Ailus wrote:
> > Hi Hans,
> > 
> > Many thanks for reviewing these.
> > 
> > On Mon, Feb 05, 2024 at 03:56:22PM +0100, Hans Verkuil wrote:
> >> On 20/12/2023 11:37, Sakari Ailus wrote:
> >>> The video device depends on the existence of its media device --- if there
> >>> is one. Acquire a reference to it.
> >>>
> >>> Note that when the media device release callback is used, then the V4L2
> >>> device release callback is ignored and a warning is issued if both are
> >>> set.
> >>>
> >>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> >>> ---
> >>>  drivers/media/v4l2-core/v4l2-dev.c | 51 ++++++++++++++++++++----------
> >>>  1 file changed, 34 insertions(+), 17 deletions(-)
> >>>
> >>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> >>> index d13954bd31fd..c1e4995eaf5c 100644
> >>> --- a/drivers/media/v4l2-core/v4l2-dev.c
> >>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
> >>> @@ -176,6 +176,11 @@ static void v4l2_device_release(struct device *cd)
> >>>  {
> >>>  	struct video_device *vdev = to_video_device(cd);
> >>>  	struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
> >>> +	bool v4l2_dev_has_release = v4l2_dev->release;
> >>> +#ifdef CONFIG_MEDIA_CONTROLLER
> >>> +	struct media_device *mdev = v4l2_dev->mdev;
> >>> +	bool mdev_has_release = mdev && mdev->ops && mdev->ops->release;
> >>> +#endif
> >>>  
> >>>  	mutex_lock(&videodev_lock);
> >>>  	if (WARN_ON(video_devices[vdev->minor] != vdev)) {
> >>> @@ -198,8 +203,8 @@ static void v4l2_device_release(struct device *cd)
> >>>  
> >>>  	mutex_unlock(&videodev_lock);
> >>>  
> >>> -#if defined(CONFIG_MEDIA_CONTROLLER)
> >>> -	if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
> >>> +#ifdef CONFIG_MEDIA_CONTROLLER
> >>> +	if (mdev && vdev->vfl_dir != VFL_DIR_M2M) {
> >>>  		/* Remove interfaces and interface links */
> >>>  		media_devnode_remove(vdev->intf_devnode);
> >>>  		if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
> >>> @@ -207,23 +212,31 @@ static void v4l2_device_release(struct device *cd)
> >>>  	}
> >>>  #endif
> >>>  
> >>> -	/* Do not call v4l2_device_put if there is no release callback set.
> >>> -	 * Drivers that have no v4l2_device release callback might free the
> >>> -	 * v4l2_dev instance in the video_device release callback below, so we
> >>> -	 * must perform this check here.
> >>> -	 *
> >>> -	 * TODO: In the long run all drivers that use v4l2_device should use the
> >>> -	 * v4l2_device release callback. This check will then be unnecessary.
> >>> -	 */
> >>> -	if (v4l2_dev->release == NULL)
> >>> -		v4l2_dev = NULL;
> >>> -
> >>>  	/* Release video_device and perform other
> >>>  	   cleanups as needed. */
> >>>  	vdev->release(vdev);
> >>>  
> >>> -	/* Decrease v4l2_device refcount */
> >>> -	if (v4l2_dev)
> >>> +#ifdef CONFIG_MEDIA_CONTROLLER
> >>> +	if (mdev)
> >>> +		media_device_put(mdev);
> >>> +
> >>> +	/*
> >>> +	 * Generally both struct media_device and struct v4l2_device are
> >>> +	 * embedded in the same driver's context struct so having a release
> >>> +	 * callback in both is a bug.
> >>> +	 */
> >>> +	WARN_ON(v4l2_dev_has_release && mdev_has_release);
> >>
> >> How about:
> >>
> >> 	if (WARN_ON(v4l2_dev_has_release && mdev_has_release))
> >> 		v4l2_dev_has_release = false;
> >>
> >>> +#endif
> >>> +
> >>> +	/*
> >>> +	 * Decrease v4l2_device refcount, but only if the media device doesn't
> >>> +	 * have a release callback.
> >>> +	 */
> >>> +	if (v4l2_dev_has_release
> >>> +#ifdef CONFIG_MEDIA_CONTROLLER
> >>> +	    && !mdev_has_release
> >>> +#endif
> >>> +	    )
> >>
> >> Then this change is no longer needed.
> > 
> > Good idea.
> > 
> > I'll also rename v4l2_dev_has_release as v4l2_dev_call_release.
> > 
> >>
> >> General question: do we have drivers today that set both release functions?
> >> Because that would now cause a WARN in the kernel log with this patch.
> > 
> > Indeed, the intention is to be vocal about it.
> > 
> > The only user of the v4l2_device release function I could find is
> > drivers/media/radio/dsbr100.c . I may have missed some but it certainly
> > isn't commonly used. Maybe we could try to drop refcounting from
> > v4l2_device later on?
> 
> There are a lot more drivers that use this. A quick grep shows gspca, hackrf,
> usbtv, pwc, au0828 and more.
> 
> git grep v4l2_dev.*release.*= drivers/media/
> 
> Currently it is the only way to properly release drivers that create multiple
> video (or other) devices.

I mistakenly grepped for ->release, .release is actually more common. I'll
check how this is currently being used.

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 23/29] media: vimc: Release resources on media device release
  2024-02-21 11:40         ` Sakari Ailus
@ 2024-02-21 11:48           ` Hans Verkuil
  2024-02-21 12:02             ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Hans Verkuil @ 2024-02-21 11:48 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, laurent.pinchart

On 21/02/2024 12:40, Sakari Ailus wrote:
> Hi Hans,
> 
> On Wed, Feb 21, 2024 at 12:19:21PM +0100, Hans Verkuil wrote:
>> On 21/02/2024 11:53, Sakari Ailus wrote:
>>> Hi Hans,
>>>
>>> On Mon, Feb 05, 2024 at 04:02:24PM +0100, Hans Verkuil wrote:
>>>> On 20/12/2023 11:37, Sakari Ailus wrote:
>>>>> Release all the resources when the media device is related, moving away
>>>
>>> s/related/released/
>>>
>>>>> form the struct v4l2_device used for that purpose.
>>>>
>>>> form -> from
>>>
>>> Yes.
>>>
>>>>
>>>>>
>>>>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
>>>>> ---
>>>>>  drivers/media/test-drivers/vimc/vimc-core.c | 15 +++++++++------
>>>>>  1 file changed, 9 insertions(+), 6 deletions(-)
>>>>>
>>>>> diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c
>>>>> index af127476e920..3e59f8c256c7 100644
>>>>> --- a/drivers/media/test-drivers/vimc/vimc-core.c
>>>>> +++ b/drivers/media/test-drivers/vimc/vimc-core.c
>>>>> @@ -264,13 +264,12 @@ static int vimc_add_subdevs(struct vimc_device *vimc)
>>>>>  	return 0;
>>>>>  }
>>>>>  
>>>>> -static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev)
>>>>> +static void vimc_mdev_release(struct media_device *mdev)
>>>>>  {
>>>>>  	struct vimc_device *vimc =
>>>>> -		container_of(v4l2_dev, struct vimc_device, v4l2_dev);
>>>>> +		container_of_const(mdev, struct vimc_device, mdev);
>>>>
>>>> Why this change?
>>>
>>> I changed the line already. There's no reason to continue using
>>> container_of() instead of container_of_const() that takes const-ness into
>>> account, too.
>>
>> But neither vimc nor mdev can be const anyway, that would make no sense
>> here.
> 
> Neither is const, true. Yet container_of_const() is preferred over

Says who?

It makes sense in generic defines that use it, e.g.:

drivers/base/firmware_loader/sysfs.h:#define to_fw_sysfs(__dev) container_of_const(__dev, struct fw_sysfs, dev)

That way it can handle both const and non-const __dev pointers.

In cases where this doesn't come into play I think there is no need to
make code changes. Perhaps when writing new code it might make sense to
use it, but changing it in existing code, esp. as part of a patch that
deals with something else entirely, seems just unnecessary churn.

I won't block this, but I recommend just dropping this change in this patch.

Regards,

	Hans

> container_of(), due to the fact that it does take const-ness into account.
> container_of() should really be avoided.
> 
> I'll add this to the commit message as Laurent suggested.
> 


^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 26/29] media: mc: Maintain a list of open file handles in a media device
  2024-02-05 15:41         ` Laurent Pinchart
@ 2024-02-21 11:53           ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21 11:53 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: Hans Verkuil, linux-media

Hi Laurent, Hans,

On Mon, Feb 05, 2024 at 05:41:36PM +0200, Laurent Pinchart wrote:
> On Mon, Feb 05, 2024 at 04:32:44PM +0100, Hans Verkuil wrote:
> > On 05/02/2024 16:16, Laurent Pinchart wrote:
> > > On Mon, Feb 05, 2024 at 04:11:43PM +0100, Hans Verkuil wrote:
> > >> On 20/12/2023 11:37, Sakari Ailus wrote:
> > >>> The list of file handles is needed to deliver media events as well as for
> > >>> other purposes in the future.
> > >>>
> > >>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > >>> ---
> > >>>  drivers/media/mc/mc-device.c  | 23 ++++++++++++++++++++++-
> > >>>  drivers/media/mc/mc-devnode.c |  2 +-
> > >>>  include/media/media-devnode.h |  4 +++-
> > >>>  3 files changed, 26 insertions(+), 3 deletions(-)
> > >>>
> > >>> diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
> > >>> index 67a39cb63f89..9cc055deeec9 100644
> > >>> --- a/drivers/media/mc/mc-device.c
> > >>> +++ b/drivers/media/mc/mc-device.c
> > >>> @@ -45,9 +45,11 @@ static inline void __user *media_get_uptr(__u64 arg)
> > >>>  	return (void __user *)(uintptr_t)arg;
> > >>>  }
> > >>>  
> > >>> -static int media_device_open(struct file *filp)
> > >>> +static int media_device_open(struct media_devnode *devnode, struct file *filp)
> > >>>  {
> > >>> +	struct media_device *mdev = to_media_device(devnode);
> > >>>  	struct media_device_fh *fh;
> > >>> +	unsigned long flags;
> > >>>  
> > >>>  	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
> > >>>  	if (!fh)
> > >>> @@ -55,12 +57,23 @@ static int media_device_open(struct file *filp)
> > >>>  
> > >>>  	filp->private_data = &fh->fh;
> > >>>  
> > >>> +	spin_lock_irqsave(&mdev->fh_list_lock, flags);
> > >>
> > >> The only reason for using the irqsave variant is because we want
> > >> to support events in the future, and those can be sent in irq context.
> > > 
> > > Even in that case, would media_device_open() ever be called from
> > > interrupt context ? spin_lock_irqsave() is only needed if you don't know
> > > which context the function can be called from. If we know we'll be
> > > called from interruptible context only, you can use spin_lock_irq()
> > > instead.
> > 
> > Someone can call open() while at the same time the kernel sends a
> > media event from interrupt context. Such an event function will walk
> > over the fh_list. The irqsave here is meant to ensure that no event
> > interrupt can run while we add our fh to the fh list.
> 
> You don't need spin_lock_irqsave() for that, spin_lock_irq() is enough.
> In your interrupt handler, you need spin_lock() only.
> spin_lock_irqsave() is for places that can be called both from
> interruptible and non-interruptible contexts.

I'll address this in v3.

-- 
Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 23/29] media: vimc: Release resources on media device release
  2024-02-21 11:48           ` Hans Verkuil
@ 2024-02-21 12:02             ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21 12:02 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, laurent.pinchart

Hi Hans,

On Wed, Feb 21, 2024 at 12:48:51PM +0100, Hans Verkuil wrote:
> On 21/02/2024 12:40, Sakari Ailus wrote:
> > Hi Hans,
> > 
> > On Wed, Feb 21, 2024 at 12:19:21PM +0100, Hans Verkuil wrote:
> >> On 21/02/2024 11:53, Sakari Ailus wrote:
> >>> Hi Hans,
> >>>
> >>> On Mon, Feb 05, 2024 at 04:02:24PM +0100, Hans Verkuil wrote:
> >>>> On 20/12/2023 11:37, Sakari Ailus wrote:
> >>>>> Release all the resources when the media device is related, moving away
> >>>
> >>> s/related/released/
> >>>
> >>>>> form the struct v4l2_device used for that purpose.
> >>>>
> >>>> form -> from
> >>>
> >>> Yes.
> >>>
> >>>>
> >>>>>
> >>>>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> >>>>> ---
> >>>>>  drivers/media/test-drivers/vimc/vimc-core.c | 15 +++++++++------
> >>>>>  1 file changed, 9 insertions(+), 6 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c
> >>>>> index af127476e920..3e59f8c256c7 100644
> >>>>> --- a/drivers/media/test-drivers/vimc/vimc-core.c
> >>>>> +++ b/drivers/media/test-drivers/vimc/vimc-core.c
> >>>>> @@ -264,13 +264,12 @@ static int vimc_add_subdevs(struct vimc_device *vimc)
> >>>>>  	return 0;
> >>>>>  }
> >>>>>  
> >>>>> -static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev)
> >>>>> +static void vimc_mdev_release(struct media_device *mdev)
> >>>>>  {
> >>>>>  	struct vimc_device *vimc =
> >>>>> -		container_of(v4l2_dev, struct vimc_device, v4l2_dev);
> >>>>> +		container_of_const(mdev, struct vimc_device, mdev);
> >>>>
> >>>> Why this change?
> >>>
> >>> I changed the line already. There's no reason to continue using
> >>> container_of() instead of container_of_const() that takes const-ness into
> >>> account, too.
> >>
> >> But neither vimc nor mdev can be const anyway, that would make no sense
> >> here.
> > 
> > Neither is const, true. Yet container_of_const() is preferred over
> 
> Says who?

container_of() documentation comes with a big, fat warning on this issue.

I can post a patch to add an explicit recommentation, too.

> 
> It makes sense in generic defines that use it, e.g.:
> 
> drivers/base/firmware_loader/sysfs.h:#define to_fw_sysfs(__dev) container_of_const(__dev, struct fw_sysfs, dev)
> 
> That way it can handle both const and non-const __dev pointers.
> 
> In cases where this doesn't come into play I think there is no need to
> make code changes. Perhaps when writing new code it might make sense to
> use it, but changing it in existing code, esp. as part of a patch that
> deals with something else entirely, seems just unnecessary churn.
> 
> I won't block this, but I recommend just dropping this change in this patch.

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device
  2024-02-21 10:43       ` Sakari Ailus
@ 2024-02-21 12:19         ` Laurent Pinchart
  2024-02-21 12:35           ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Laurent Pinchart @ 2024-02-21 12:19 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: Hans Verkuil, linux-media

On Wed, Feb 21, 2024 at 10:43:47AM +0000, Sakari Ailus wrote:
> Hi Laurent,
> 
> Thank you for reviewing the set!
> 
> On Wed, Feb 07, 2024 at 01:13:44PM +0200, Laurent Pinchart wrote:
> > On Mon, Feb 05, 2024 at 03:56:22PM +0100, Hans Verkuil wrote:
> > > On 20/12/2023 11:37, Sakari Ailus wrote:
> > > > The video device depends on the existence of its media device --- if there
> > > > is one. Acquire a reference to it.
> > > > 
> > > > Note that when the media device release callback is used, then the V4L2
> > > > device release callback is ignored and a warning is issued if both are
> > > > set.
> > 
> > Why is that ? The two are distinct objects, why can't they both have a
> > release function ?
> 
> You could, in principle, but in practice both of the structs are part of
> the same driver's device context struct which is a single allocation. You
> can only have a single release callback for it.

If both release callbacks freed the same data structure, that would
indeed be a problem. There could be other use cases though. For
instance, in the uvcvideo driver, the top-level structure is
reference-counted, and the release callbacks of the video devices
decrement that reference count. I don't expect drivers to do something
similar with media_device and v4l2_device, but I'm not sure if we should
forbid it completely. If we do, I would then rather deprecate the
release callback of v4l2_device completely.

> > > > 
> > > > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > > > ---
> > > >  drivers/media/v4l2-core/v4l2-dev.c | 51 ++++++++++++++++++++----------
> > > >  1 file changed, 34 insertions(+), 17 deletions(-)
> > > > 
> > > > diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> > > > index d13954bd31fd..c1e4995eaf5c 100644
> > > > --- a/drivers/media/v4l2-core/v4l2-dev.c
> > > > +++ b/drivers/media/v4l2-core/v4l2-dev.c
> > > > @@ -176,6 +176,11 @@ static void v4l2_device_release(struct device *cd)
> > > >  {
> > > >  	struct video_device *vdev = to_video_device(cd);
> > > >  	struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
> > > > +	bool v4l2_dev_has_release = v4l2_dev->release;
> > > > +#ifdef CONFIG_MEDIA_CONTROLLER
> > > > +	struct media_device *mdev = v4l2_dev->mdev;
> > > > +	bool mdev_has_release = mdev && mdev->ops && mdev->ops->release;
> > > > +#endif
> > > >  
> > > >  	mutex_lock(&videodev_lock);
> > > >  	if (WARN_ON(video_devices[vdev->minor] != vdev)) {
> > > > @@ -198,8 +203,8 @@ static void v4l2_device_release(struct device *cd)
> > > >  
> > > >  	mutex_unlock(&videodev_lock);
> > > >  
> > > > -#if defined(CONFIG_MEDIA_CONTROLLER)
> > > > -	if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
> > > > +#ifdef CONFIG_MEDIA_CONTROLLER
> > > > +	if (mdev && vdev->vfl_dir != VFL_DIR_M2M) {
> > > >  		/* Remove interfaces and interface links */
> > > >  		media_devnode_remove(vdev->intf_devnode);
> > > >  		if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
> > > > @@ -207,23 +212,31 @@ static void v4l2_device_release(struct device *cd)
> > > >  	}
> > > >  #endif
> > > >  
> > > > -	/* Do not call v4l2_device_put if there is no release callback set.
> > > > -	 * Drivers that have no v4l2_device release callback might free the
> > > > -	 * v4l2_dev instance in the video_device release callback below, so we
> > > > -	 * must perform this check here.
> > > > -	 *
> > > > -	 * TODO: In the long run all drivers that use v4l2_device should use the
> > > > -	 * v4l2_device release callback. This check will then be unnecessary.
> > > > -	 */
> > > > -	if (v4l2_dev->release == NULL)
> > > > -		v4l2_dev = NULL;
> > > > -
> > > >  	/* Release video_device and perform other
> > > >  	   cleanups as needed. */
> > > >  	vdev->release(vdev);
> > > >  
> > > > -	/* Decrease v4l2_device refcount */
> > > > -	if (v4l2_dev)
> > > > +#ifdef CONFIG_MEDIA_CONTROLLER
> > > > +	if (mdev)
> > > > +		media_device_put(mdev);
> > > > +
> > > > +	/*
> > > > +	 * Generally both struct media_device and struct v4l2_device are
> > > > +	 * embedded in the same driver's context struct so having a release
> > > > +	 * callback in both is a bug.
> > > > +	 */
> > > > +	WARN_ON(v4l2_dev_has_release && mdev_has_release);
> > > 
> > > How about:
> > > 
> > > 	if (WARN_ON(v4l2_dev_has_release && mdev_has_release))
> > > 		v4l2_dev_has_release = false;
> > > 
> > > > +#endif
> > > > +
> > > > +	/*
> > > > +	 * Decrease v4l2_device refcount, but only if the media device doesn't
> > > > +	 * have a release callback.
> > > > +	 */
> > > > +	if (v4l2_dev_has_release
> > > > +#ifdef CONFIG_MEDIA_CONTROLLER
> > > > +	    && !mdev_has_release
> > > > +#endif
> > > > +	    )
> > > 
> > > Then this change is no longer needed.
> > > 
> > > General question: do we have drivers today that set both release functions?
> > > Because that would now cause a WARN in the kernel log with this patch.
> > > 
> > > >  		v4l2_device_put(v4l2_dev);
> > > >  }
> > > >  
> > > > @@ -792,11 +805,14 @@ static int video_register_media_controller(struct video_device *vdev)
> > > >  	u32 intf_type;
> > > >  	int ret;
> > > >  
> > > > -	/* Memory-to-memory devices are more complex and use
> > > > +	/*
> > > > +	 * Memory-to-memory devices are more complex and use
> > > >  	 * their own function to register its mc entities.
> > 
> > If you fix the comment style as a drive-by change, you could as well
> > reflow it to 80 columns.
> 
> I'll update this for the next version.
> 
> > > >  	 */
> > > > -	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M)
> > > > +	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M) {
> > > > +		media_device_get(vdev->v4l2_dev->mdev);
> > > >  		return 0;
> > > > +	}
> > > >  
> > > >  	vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
> > > >  	vdev->entity.function = MEDIA_ENT_F_UNKNOWN;
> > > > @@ -875,6 +891,7 @@ static int video_register_media_controller(struct video_device *vdev)
> > > >  
> > > >  	/* FIXME: how to create the other interface links? */
> > > >  
> > > > +	media_device_get(vdev->v4l2_dev->mdev);
> > > >  #endif
> > > >  	return 0;
> > > >  }

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device
  2024-02-21 12:19         ` Laurent Pinchart
@ 2024-02-21 12:35           ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21 12:35 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: Hans Verkuil, linux-media

Hi Laurent,

On Wed, Feb 21, 2024 at 02:19:56PM +0200, Laurent Pinchart wrote:
> On Wed, Feb 21, 2024 at 10:43:47AM +0000, Sakari Ailus wrote:
> > Hi Laurent,
> > 
> > Thank you for reviewing the set!
> > 
> > On Wed, Feb 07, 2024 at 01:13:44PM +0200, Laurent Pinchart wrote:
> > > On Mon, Feb 05, 2024 at 03:56:22PM +0100, Hans Verkuil wrote:
> > > > On 20/12/2023 11:37, Sakari Ailus wrote:
> > > > > The video device depends on the existence of its media device --- if there
> > > > > is one. Acquire a reference to it.
> > > > > 
> > > > > Note that when the media device release callback is used, then the V4L2
> > > > > device release callback is ignored and a warning is issued if both are
> > > > > set.
> > > 
> > > Why is that ? The two are distinct objects, why can't they both have a
> > > release function ?
> > 
> > You could, in principle, but in practice both of the structs are part of
> > the same driver's device context struct which is a single allocation. You
> > can only have a single release callback for it.
> 
> If both release callbacks freed the same data structure, that would
> indeed be a problem. There could be other use cases though. For
> instance, in the uvcvideo driver, the top-level structure is
> reference-counted, and the release callbacks of the video devices
> decrement that reference count. I don't expect drivers to do something
> similar with media_device and v4l2_device, but I'm not sure if we should
> forbid it completely. If we do, I would then rather deprecate the
> release callback of v4l2_device completely.

There are quite a few drivers using the struct v4l2_device's release
callback, it seems. We very probably can't get rid of it except possibly in
the long term.

The documentation already takes this into account, see patch "media:
Documentation: Document how Media device resources are released".

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 04/29] media: mc: utilize new cdev_device_add helper function
  2024-02-07  9:51     ` Laurent Pinchart
@ 2024-02-21 12:55       ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-02-21 12:55 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil

Hi Laurent,

On Wed, Feb 07, 2024 at 11:51:37AM +0200, Laurent Pinchart wrote:
> On Wed, Feb 07, 2024 at 11:38:18AM +0200, Laurent Pinchart wrote:
> > Hi Sakari,
> > 
> > Thank you for the patch.
> > 
> > On Wed, Dec 20, 2023 at 12:36:48PM +0200, Sakari Ailus wrote:
> > > From: Logan Gunthorpe <logang@deltatee.com>
> > > 
> > > Replace the open coded registration of the cdev and dev with the
> > > new device_add_cdev() helper. The helper replaces a common pattern by
> > > taking the proper reference against the parent device and adding both
> > > the cdev and the device.
> > > 
> > > Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
> > > Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
> > > Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> > 
> > This reapplies a commit you've reverted in 02/29 in this series. I
> > understand this is done to be able to apply the revert in 03/29 cleanly.
> > Given that those three patches are consecutive, wouldn't it be better to
> > squash 02/29, 03/29 and 04/29, with the commit message of 03/29 ?
> > Otherwise, I would at least drop the Acked-by and Reviewed-by tags in
> > the patches you reapply, as they've been reviewed in a different
> > context.
> > 
> > The same applies to patches 05/29, 06/29 and 07/29.
> 
> And especially to those patches actually. 06/29 has a single line change
> for the uvcvideo driver, the revert in 05/29 and re-revert in 07/29 seem
> overkill.

The revert can't be applied as-is otherwise but I'm fine merging them.

> 
> It would also be nice to expand the commit messages of 03/29 and 06/29
> to explain why the revert are needed.

I can add that. These are basically improvements in the code but depend on
commit a087ce704b80. Conceptually, it'd get quite difficult as what's
really needed here is to get back to an earlier state, this is not
development over the said commit.

I'll drop the acks.

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device
  2024-02-21 11:44         ` Sakari Ailus
@ 2024-03-05  7:43           ` Sakari Ailus
  2024-03-05  7:46             ` Hans Verkuil
  0 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2024-03-05  7:43 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, laurent.pinchart

Hi Hans,

On Wed, Feb 21, 2024 at 11:44:59AM +0000, Sakari Ailus wrote:
> Hi Hans,
> 
> On Wed, Feb 21, 2024 at 11:51:08AM +0100, Hans Verkuil wrote:
> > On 21/02/2024 11:40, Sakari Ailus wrote:
> > > Hi Hans,
> > > 
> > > Many thanks for reviewing these.
> > > 
> > > On Mon, Feb 05, 2024 at 03:56:22PM +0100, Hans Verkuil wrote:
> > >> On 20/12/2023 11:37, Sakari Ailus wrote:
> > >>> The video device depends on the existence of its media device --- if there
> > >>> is one. Acquire a reference to it.
> > >>>
> > >>> Note that when the media device release callback is used, then the V4L2
> > >>> device release callback is ignored and a warning is issued if both are
> > >>> set.
> > >>>
> > >>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > >>> ---
> > >>>  drivers/media/v4l2-core/v4l2-dev.c | 51 ++++++++++++++++++++----------
> > >>>  1 file changed, 34 insertions(+), 17 deletions(-)
> > >>>
> > >>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> > >>> index d13954bd31fd..c1e4995eaf5c 100644
> > >>> --- a/drivers/media/v4l2-core/v4l2-dev.c
> > >>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
> > >>> @@ -176,6 +176,11 @@ static void v4l2_device_release(struct device *cd)
> > >>>  {
> > >>>  	struct video_device *vdev = to_video_device(cd);
> > >>>  	struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
> > >>> +	bool v4l2_dev_has_release = v4l2_dev->release;
> > >>> +#ifdef CONFIG_MEDIA_CONTROLLER
> > >>> +	struct media_device *mdev = v4l2_dev->mdev;
> > >>> +	bool mdev_has_release = mdev && mdev->ops && mdev->ops->release;
> > >>> +#endif
> > >>>  
> > >>>  	mutex_lock(&videodev_lock);
> > >>>  	if (WARN_ON(video_devices[vdev->minor] != vdev)) {
> > >>> @@ -198,8 +203,8 @@ static void v4l2_device_release(struct device *cd)
> > >>>  
> > >>>  	mutex_unlock(&videodev_lock);
> > >>>  
> > >>> -#if defined(CONFIG_MEDIA_CONTROLLER)
> > >>> -	if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
> > >>> +#ifdef CONFIG_MEDIA_CONTROLLER
> > >>> +	if (mdev && vdev->vfl_dir != VFL_DIR_M2M) {
> > >>>  		/* Remove interfaces and interface links */
> > >>>  		media_devnode_remove(vdev->intf_devnode);
> > >>>  		if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
> > >>> @@ -207,23 +212,31 @@ static void v4l2_device_release(struct device *cd)
> > >>>  	}
> > >>>  #endif
> > >>>  
> > >>> -	/* Do not call v4l2_device_put if there is no release callback set.
> > >>> -	 * Drivers that have no v4l2_device release callback might free the
> > >>> -	 * v4l2_dev instance in the video_device release callback below, so we
> > >>> -	 * must perform this check here.
> > >>> -	 *
> > >>> -	 * TODO: In the long run all drivers that use v4l2_device should use the
> > >>> -	 * v4l2_device release callback. This check will then be unnecessary.
> > >>> -	 */
> > >>> -	if (v4l2_dev->release == NULL)
> > >>> -		v4l2_dev = NULL;
> > >>> -
> > >>>  	/* Release video_device and perform other
> > >>>  	   cleanups as needed. */
> > >>>  	vdev->release(vdev);
> > >>>  
> > >>> -	/* Decrease v4l2_device refcount */
> > >>> -	if (v4l2_dev)
> > >>> +#ifdef CONFIG_MEDIA_CONTROLLER
> > >>> +	if (mdev)
> > >>> +		media_device_put(mdev);
> > >>> +
> > >>> +	/*
> > >>> +	 * Generally both struct media_device and struct v4l2_device are
> > >>> +	 * embedded in the same driver's context struct so having a release
> > >>> +	 * callback in both is a bug.
> > >>> +	 */
> > >>> +	WARN_ON(v4l2_dev_has_release && mdev_has_release);
> > >>
> > >> How about:
> > >>
> > >> 	if (WARN_ON(v4l2_dev_has_release && mdev_has_release))
> > >> 		v4l2_dev_has_release = false;
> > >>
> > >>> +#endif
> > >>> +
> > >>> +	/*
> > >>> +	 * Decrease v4l2_device refcount, but only if the media device doesn't
> > >>> +	 * have a release callback.
> > >>> +	 */
> > >>> +	if (v4l2_dev_has_release
> > >>> +#ifdef CONFIG_MEDIA_CONTROLLER
> > >>> +	    && !mdev_has_release
> > >>> +#endif
> > >>> +	    )
> > >>
> > >> Then this change is no longer needed.
> > > 
> > > Good idea.
> > > 
> > > I'll also rename v4l2_dev_has_release as v4l2_dev_call_release.
> > > 
> > >>
> > >> General question: do we have drivers today that set both release functions?
> > >> Because that would now cause a WARN in the kernel log with this patch.
> > > 
> > > Indeed, the intention is to be vocal about it.
> > > 
> > > The only user of the v4l2_device release function I could find is
> > > drivers/media/radio/dsbr100.c . I may have missed some but it certainly
> > > isn't commonly used. Maybe we could try to drop refcounting from
> > > v4l2_device later on?
> > 
> > There are a lot more drivers that use this. A quick grep shows gspca, hackrf,
> > usbtv, pwc, au0828 and more.
> > 
> > git grep v4l2_dev.*release.*= drivers/media/
> > 
> > Currently it is the only way to properly release drivers that create multiple
> > video (or other) devices.
> 
> I mistakenly grepped for ->release, .release is actually more common. I'll
> check how this is currently being used.

Getting back to the topic---indeed the V4L2 device release function is used
by a number of drivers today. Moving to the Media device release function
is no small task: I checked some drivers and while releasing the resources
is centralised in this case, unregistering the interfaces and releasing
actual resources may be intertwined so that fixing this requires reworking
much of the driver code. It's better to leave this for driver authors or at
least someone who has the hardware.

-- 
Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device
  2024-03-05  7:43           ` Sakari Ailus
@ 2024-03-05  7:46             ` Hans Verkuil
  0 siblings, 0 replies; 92+ messages in thread
From: Hans Verkuil @ 2024-03-05  7:46 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, laurent.pinchart

On 05/03/2024 8:43 am, Sakari Ailus wrote:
> Hi Hans,
> 
> On Wed, Feb 21, 2024 at 11:44:59AM +0000, Sakari Ailus wrote:
>> Hi Hans,
>>
>> On Wed, Feb 21, 2024 at 11:51:08AM +0100, Hans Verkuil wrote:
>>> On 21/02/2024 11:40, Sakari Ailus wrote:
>>>> Hi Hans,
>>>>
>>>> Many thanks for reviewing these.
>>>>
>>>> On Mon, Feb 05, 2024 at 03:56:22PM +0100, Hans Verkuil wrote:
>>>>> On 20/12/2023 11:37, Sakari Ailus wrote:
>>>>>> The video device depends on the existence of its media device --- if there
>>>>>> is one. Acquire a reference to it.
>>>>>>
>>>>>> Note that when the media device release callback is used, then the V4L2
>>>>>> device release callback is ignored and a warning is issued if both are
>>>>>> set.
>>>>>>
>>>>>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
>>>>>> ---
>>>>>>  drivers/media/v4l2-core/v4l2-dev.c | 51 ++++++++++++++++++++----------
>>>>>>  1 file changed, 34 insertions(+), 17 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
>>>>>> index d13954bd31fd..c1e4995eaf5c 100644
>>>>>> --- a/drivers/media/v4l2-core/v4l2-dev.c
>>>>>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
>>>>>> @@ -176,6 +176,11 @@ static void v4l2_device_release(struct device *cd)
>>>>>>  {
>>>>>>  	struct video_device *vdev = to_video_device(cd);
>>>>>>  	struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
>>>>>> +	bool v4l2_dev_has_release = v4l2_dev->release;
>>>>>> +#ifdef CONFIG_MEDIA_CONTROLLER
>>>>>> +	struct media_device *mdev = v4l2_dev->mdev;
>>>>>> +	bool mdev_has_release = mdev && mdev->ops && mdev->ops->release;
>>>>>> +#endif
>>>>>>  
>>>>>>  	mutex_lock(&videodev_lock);
>>>>>>  	if (WARN_ON(video_devices[vdev->minor] != vdev)) {
>>>>>> @@ -198,8 +203,8 @@ static void v4l2_device_release(struct device *cd)
>>>>>>  
>>>>>>  	mutex_unlock(&videodev_lock);
>>>>>>  
>>>>>> -#if defined(CONFIG_MEDIA_CONTROLLER)
>>>>>> -	if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
>>>>>> +#ifdef CONFIG_MEDIA_CONTROLLER
>>>>>> +	if (mdev && vdev->vfl_dir != VFL_DIR_M2M) {
>>>>>>  		/* Remove interfaces and interface links */
>>>>>>  		media_devnode_remove(vdev->intf_devnode);
>>>>>>  		if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
>>>>>> @@ -207,23 +212,31 @@ static void v4l2_device_release(struct device *cd)
>>>>>>  	}
>>>>>>  #endif
>>>>>>  
>>>>>> -	/* Do not call v4l2_device_put if there is no release callback set.
>>>>>> -	 * Drivers that have no v4l2_device release callback might free the
>>>>>> -	 * v4l2_dev instance in the video_device release callback below, so we
>>>>>> -	 * must perform this check here.
>>>>>> -	 *
>>>>>> -	 * TODO: In the long run all drivers that use v4l2_device should use the
>>>>>> -	 * v4l2_device release callback. This check will then be unnecessary.
>>>>>> -	 */
>>>>>> -	if (v4l2_dev->release == NULL)
>>>>>> -		v4l2_dev = NULL;
>>>>>> -
>>>>>>  	/* Release video_device and perform other
>>>>>>  	   cleanups as needed. */
>>>>>>  	vdev->release(vdev);
>>>>>>  
>>>>>> -	/* Decrease v4l2_device refcount */
>>>>>> -	if (v4l2_dev)
>>>>>> +#ifdef CONFIG_MEDIA_CONTROLLER
>>>>>> +	if (mdev)
>>>>>> +		media_device_put(mdev);
>>>>>> +
>>>>>> +	/*
>>>>>> +	 * Generally both struct media_device and struct v4l2_device are
>>>>>> +	 * embedded in the same driver's context struct so having a release
>>>>>> +	 * callback in both is a bug.
>>>>>> +	 */
>>>>>> +	WARN_ON(v4l2_dev_has_release && mdev_has_release);
>>>>>
>>>>> How about:
>>>>>
>>>>> 	if (WARN_ON(v4l2_dev_has_release && mdev_has_release))
>>>>> 		v4l2_dev_has_release = false;
>>>>>
>>>>>> +#endif
>>>>>> +
>>>>>> +	/*
>>>>>> +	 * Decrease v4l2_device refcount, but only if the media device doesn't
>>>>>> +	 * have a release callback.
>>>>>> +	 */
>>>>>> +	if (v4l2_dev_has_release
>>>>>> +#ifdef CONFIG_MEDIA_CONTROLLER
>>>>>> +	    && !mdev_has_release
>>>>>> +#endif
>>>>>> +	    )
>>>>>
>>>>> Then this change is no longer needed.
>>>>
>>>> Good idea.
>>>>
>>>> I'll also rename v4l2_dev_has_release as v4l2_dev_call_release.
>>>>
>>>>>
>>>>> General question: do we have drivers today that set both release functions?
>>>>> Because that would now cause a WARN in the kernel log with this patch.
>>>>
>>>> Indeed, the intention is to be vocal about it.
>>>>
>>>> The only user of the v4l2_device release function I could find is
>>>> drivers/media/radio/dsbr100.c . I may have missed some but it certainly
>>>> isn't commonly used. Maybe we could try to drop refcounting from
>>>> v4l2_device later on?
>>>
>>> There are a lot more drivers that use this. A quick grep shows gspca, hackrf,
>>> usbtv, pwc, au0828 and more.
>>>
>>> git grep v4l2_dev.*release.*= drivers/media/
>>>
>>> Currently it is the only way to properly release drivers that create multiple
>>> video (or other) devices.
>>
>> I mistakenly grepped for ->release, .release is actually more common. I'll
>> check how this is currently being used.
> 
> Getting back to the topic---indeed the V4L2 device release function is used
> by a number of drivers today. Moving to the Media device release function
> is no small task: I checked some drivers and while releasing the resources
> is centralised in this case, unregistering the interfaces and releasing
> actual resources may be intertwined so that fixing this requires reworking
> much of the driver code. It's better to leave this for driver authors or at
> least someone who has the hardware.
> 

Indeed. Most if not all of these drivers do not create a media device anyway.
Now, if you see drivers that have a media device AND use the v4l2_device
release callback, then let me know. If I have the hardware, then I can try
and switch it to using the media device callback since that would make more
sense.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 09/29] media: mc: Do not call cdev_device_del() if cdev_device_add() fails
  2024-02-07  9:57   ` Laurent Pinchart
@ 2024-03-05  8:13     ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-03-05  8:13 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil

Hi Laurent,

On Wed, Feb 07, 2024 at 11:57:25AM +0200, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.
> 
> On Wed, Dec 20, 2023 at 12:36:53PM +0200, Sakari Ailus wrote:
> > cdev_device_del() is the right function to remove a device when
> > cdev_device_add() succeeds. If it does not, however, put_device() needs to
> > be used instead. Fix this.
> 
> Where's the put_device() call ?

Seems to have gone missing at some point.

It was added though by "media: mc: Split initialising and adding media
devnode" a little later as this got moved to mc-device.c, so fixing this
here makes difference until that patch.

I'll address this for v3.

> 
> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> > ---
> >  drivers/media/mc/mc-devnode.c | 1 -
> >  1 file changed, 1 deletion(-)
> > 
> > diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> > index ce93ab9be676..7e22938dfd81 100644
> > --- a/drivers/media/mc/mc-devnode.c
> > +++ b/drivers/media/mc/mc-devnode.c
> > @@ -254,7 +254,6 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
> >  
> >  cdev_add_error:
> >  	mutex_lock(&media_devnode_lock);
> > -	cdev_device_del(&devnode->cdev, &devnode->dev);
> >  	clear_bit(devnode->minor, media_devnode_nums);
> >  	mutex_unlock(&media_devnode_lock);
> >  
> 

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 10/29] media: mc: Delete character device early
  2024-02-07 10:08   ` Laurent Pinchart
@ 2024-03-05  8:52     ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-03-05  8:52 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil

Hi Laurent,

On Wed, Feb 07, 2024 at 12:08:10PM +0200, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.
> 
> On Wed, Dec 20, 2023 at 12:36:54PM +0200, Sakari Ailus wrote:
> > The parent of the character device related to the media devnode is the
> > media devnode. Thus the character device needs to be released before the
> > media devnode's release function. Move it to unregistering of the media
> > devnode, which mirrors adding the character device in conjunction with
> > registering the media devnode.
> > 
> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> > ---
> >  drivers/media/mc/mc-devnode.c | 4 +---
> >  1 file changed, 1 insertion(+), 3 deletions(-)
> > 
> > diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> > index 7e22938dfd81..8bc7450ac144 100644
> > --- a/drivers/media/mc/mc-devnode.c
> > +++ b/drivers/media/mc/mc-devnode.c
> > @@ -51,9 +51,6 @@ static void media_devnode_release(struct device *cd)
> >  
> >  	mutex_lock(&media_devnode_lock);
> >  
> > -	/* Delete the cdev on this minor as well */
> > -	cdev_del(&devnode->cdev);
> > -
> >  	/* Mark device node number as free */
> >  	clear_bit(devnode->minor, media_devnode_nums);
> 
> Should this be moved to media_devnode_unregister() too ? It can be done
> in a separate patch.

Good question. Yes, I think that seems reasonable. The minor isn't needed
after unregistering the device.

> 
> >  
> > @@ -270,6 +267,7 @@ void media_devnode_unregister(struct media_devnode *devnode)
> >  	clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
> >  	mutex_unlock(&media_devnode_lock);
> >  
> > +	cdev_del(&devnode->cdev);
> 
> I initially wondered if this could raise with the cdev access in
> media_open(), as the media_devnode_lock is released just before calling
> cdev_dev(), but my understanding is that the dev/open race is properly
> handled in the cdev layer.
> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Thanks!

> 
> I wonder if a similar change in v4l2-dev.c would be beneficial.

Quite possibly. Let's see...

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 11/29] media: mc: Split initialising and adding media devnode
  2024-02-07 10:46   ` Laurent Pinchart
@ 2024-03-05  8:59     ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-03-05  8:59 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil

Hi Laurent,

Thanks for the review.

On Wed, Feb 07, 2024 at 12:46:13PM +0200, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.
> 
> On Wed, Dec 20, 2023 at 12:36:55PM +0200, Sakari Ailus wrote:
> > As registering a device node of an entity belonging to a media device
> 
> Did you mean s/As registering/Registering/ ?

That works better in the current context. I'll use that.

> 
> > will require a reference to the struct device. Taking that reference is
> > only possible once the device has been initialised, which took place only
> 
> s/took/takes/
> 
> > when it was registered. Split this in two, and initialise the device when
> 
> s/was/is/

This describes the behaviour before the patch. I think it makes sense to
use imperfect here.

> 
> > the media device is allocated.
> > 
> > Don't distribute the effects of these changes yet. Add media_device_get()
> > and media_device_put() first.
> 
> Don't propagate the effects of these changes to drivers yet, we want to
> expose media_device refcounting with media_device_get() and
> media_device_put() functions first.
> 
> 
> I'm not sure that's exactly what you meant though.

Yes, that's roughly what I meant, I can use the above text.

> 
> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Thanks!

> 
> > ---
> >  drivers/media/mc/mc-device.c  | 18 +++++++++++++-----
> >  drivers/media/mc/mc-devnode.c | 17 ++++++++++-------
> >  include/media/media-devnode.h | 19 ++++++++++++++-----
> >  3 files changed, 37 insertions(+), 17 deletions(-)
> > 
> > diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
> > index c0ea08a8fc31..ebf037cd5f4a 100644
> > --- a/drivers/media/mc/mc-device.c
> > +++ b/drivers/media/mc/mc-device.c
> > @@ -717,19 +717,26 @@ int __must_check __media_device_register(struct media_device *mdev,
> >  	/* Set version 0 to indicate user-space that the graph is static */
> >  	mdev->topology_version = 0;
> >  
> > +	media_devnode_init(&mdev->devnode);
> > +
> >  	ret = media_devnode_register(&mdev->devnode, owner);
> >  	if (ret < 0)
> > -		return ret;
> > +		goto out_put;
> >  
> >  	ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
> > -	if (ret < 0) {
> > -		media_devnode_unregister(&mdev->devnode);
> > -		return ret;
> > -	}
> > +	if (ret < 0)
> > +		goto out_unregister;
> >  
> >  	dev_dbg(mdev->dev, "Media device registered\n");
> >  
> >  	return 0;
> > +
> > +out_unregister:
> 
> I would name the labels err_unregister and err_put.

Sounds good.

> 
> > +	media_devnode_unregister(&mdev->devnode);
> > +out_put:
> > +	put_device(&mdev->devnode.dev);
> > +
> > +	return ret;
> >  }
> >  EXPORT_SYMBOL_GPL(__media_device_register);
> >  
> > @@ -803,6 +810,7 @@ void media_device_unregister(struct media_device *mdev)
> >  	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
> >  	dev_dbg(mdev->dev, "Media device unregistering\n");
> >  	media_devnode_unregister(&mdev->devnode);
> > +	put_device(&mdev->devnode.dev);
> >  }
> >  EXPORT_SYMBOL_GPL(media_device_unregister);
> >  
> > diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> > index 8bc7450ac144..7b17419050fb 100644
> > --- a/drivers/media/mc/mc-devnode.c
> > +++ b/drivers/media/mc/mc-devnode.c
> > @@ -204,6 +204,11 @@ static const struct file_operations media_devnode_fops = {
> >  	.llseek = no_llseek,
> >  };
> >  
> > +void media_devnode_init(struct media_devnode *devnode)
> > +{
> > +	device_initialize(&devnode->dev);
> > +}
> > +
> >  int __must_check media_devnode_register(struct media_devnode *devnode,
> >  					struct module *owner)
> >  {
> > @@ -235,7 +240,6 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
> >  	if (devnode->parent)
> >  		devnode->dev.parent = devnode->parent;
> >  	dev_set_name(&devnode->dev, "media%d", devnode->minor);
> > -	device_initialize(&devnode->dev);
> >  
> >  	/* Part 3: Add the media and character devices */
> >  	ret = cdev_device_add(&devnode->cdev, &devnode->dev);
> > @@ -267,14 +271,13 @@ void media_devnode_unregister(struct media_devnode *devnode)
> >  	clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
> >  	mutex_unlock(&media_devnode_lock);
> >  
> > -	cdev_del(&devnode->cdev);
> > -	device_unregister(&devnode->dev);
> > +	cdev_device_del(&devnode->cdev, &devnode->dev);
> >  }
> >  
> >  /*
> >   *	Initialise media for linux
> >   */
> > -static int __init media_devnode_init(void)
> > +static int __init media_devnode_module_init(void)
> >  {
> >  	int ret;
> >  
> > @@ -296,14 +299,14 @@ static int __init media_devnode_init(void)
> >  	return 0;
> >  }
> >  
> > -static void __exit media_devnode_exit(void)
> > +static void __exit media_devnode_module_exit(void)
> >  {
> >  	bus_unregister(&media_bus_type);
> >  	unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
> >  }
> >  
> > -subsys_initcall(media_devnode_init);
> > -module_exit(media_devnode_exit)
> > +subsys_initcall(media_devnode_module_init);
> > +module_exit(media_devnode_module_exit)
> >  
> >  MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
> >  MODULE_DESCRIPTION("Device node registration for media drivers");
> > diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
> > index 1117d1dfd6bf..6d46c658be21 100644
> > --- a/include/media/media-devnode.h
> > +++ b/include/media/media-devnode.h
> > @@ -90,6 +90,17 @@ struct media_devnode {
> >  /* dev to media_devnode */
> >  #define to_media_devnode(cd) container_of(cd, struct media_devnode, dev)
> >  
> > +/**
> > + * media_devnode_init - initialise a media devnode
> > + *
> > + * @devnode: struct media_devnode we want to initialise
> > + *
> > + * Initialise a media devnode. Note that after initialising the media
> > + * devnode is refcounted. Releasing references to it may be done using
> > + * put_device().
> > + */
> > +void media_devnode_init(struct media_devnode *devnode);
> > +
> >  /**
> >   * media_devnode_register - register a media device node
> >   *
> > @@ -100,11 +111,9 @@ struct media_devnode {
> >   * with the kernel. An error is returned if no free minor number can be found,
> >   * or if the registration of the device node fails.
> >   *
> > - * Zero is returned on success.
> > - *
> > - * Note that if the media_devnode_register call fails, the release() callback of
> > - * the media_devnode structure is *not* called, so the caller is responsible for
> > - * freeing any data.
> > + * Zero is returned on success. Note that in case
> > + * media_devnode_register() fails, the caller is responsible for
> > + * releasing the reference to the device using put_device().
> >   */
> >  int __must_check media_devnode_register(struct media_devnode *devnode,
> >  					struct module *owner);
> 

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 20/29] media: ipu3-cio2: Call v4l2_device_unregister() earlier
  2024-02-07 14:24   ` Laurent Pinchart
@ 2024-03-05 10:21     ` Sakari Ailus
  2024-03-05 10:22       ` Sakari Ailus
  0 siblings, 1 reply; 92+ messages in thread
From: Sakari Ailus @ 2024-03-05 10:21 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil

Hi Laurent,

On Wed, Feb 07, 2024 at 04:24:35PM +0200, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.
> 
> On Wed, Dec 20, 2023 at 12:37:04PM +0200, Sakari Ailus wrote:
> > v4l2_device_unregister() unregisters V4L2 sub-device nodes among other
> > things. Call it before releasing memory and other resources.
> 
> Please expand the commit message, it's not immediately clear why this is
> needed and what the consequences are.

Thanks for the review.

There's actually a change in APIs here as the async notifier expects the
V4L2 device to remain in place so the notifier can't be cleaned up before
unregistering the V4L2 device. Further patches in the set move cleaning up
the notifier to the release callback but I think it can well be done here,
too. So I think this patch can be dropped.

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 20/29] media: ipu3-cio2: Call v4l2_device_unregister() earlier
  2024-03-05 10:21     ` Sakari Ailus
@ 2024-03-05 10:22       ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-03-05 10:22 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil

On Tue, Mar 05, 2024 at 10:21:54AM +0000, Sakari Ailus wrote:
> Hi Laurent,
> 
> On Wed, Feb 07, 2024 at 04:24:35PM +0200, Laurent Pinchart wrote:
> > Hi Sakari,
> > 
> > Thank you for the patch.
> > 
> > On Wed, Dec 20, 2023 at 12:37:04PM +0200, Sakari Ailus wrote:
> > > v4l2_device_unregister() unregisters V4L2 sub-device nodes among other
> > > things. Call it before releasing memory and other resources.
> > 
> > Please expand the commit message, it's not immediately clear why this is
> > needed and what the consequences are.
> 
> Thanks for the review.
> 
> There's actually a change in APIs here as the async notifier expects the
> V4L2 device to remain in place so the notifier can't be cleaned up before
> unregistering the V4L2 device. Further patches in the set move cleaning up
> the notifier to the release callback but I think it can well be done here,
> too. So I think this patch can be dropped.

That went a bit too fast. The call should be moved after the notifier
cleanup.

-- 
Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 16/29] media: mc: Refcount the media device
  2024-02-07 11:08   ` Laurent Pinchart
@ 2024-03-07 10:37     ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-03-07 10:37 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil

Hi Laurent,

On Wed, Feb 07, 2024 at 01:08:48PM +0200, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.
> 
> On Wed, Dec 20, 2023 at 12:37:00PM +0200, Sakari Ailus wrote:
> > As the struct media_device embeds struct media_devnode, the lifetime of
> > that object must be that same than that of the media_device.
> > 
> > References are obtained by media_device_get() and released by
> > media_device_put(). In order to use refcounting, the driver must set the
> > release callback before calling media_device_init() on the media device.
> > 
> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> > ---
> >  drivers/media/mc/mc-device.c  | 36 +++++++++++++++++++++++++++++------
> >  drivers/media/mc/mc-devnode.c |  6 +++++-
> >  include/media/media-device.h  | 28 +++++++++++++++++++++++++++
> >  3 files changed, 63 insertions(+), 7 deletions(-)
> > 
> > diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c
> > index e6ac9b066524..bbc233e726d2 100644
> > --- a/drivers/media/mc/mc-device.c
> > +++ b/drivers/media/mc/mc-device.c
> > @@ -700,6 +700,31 @@ void media_device_unregister_entity_notify(struct media_device *mdev,
> >  }
> >  EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify);
> >  
> > +static void __media_device_release(struct media_device *mdev)
> > +{
> > +	dev_dbg(mdev->dev, "Media device released\n");
> > +
> > +	ida_destroy(&mdev->entity_internal_idx);
> > +	mdev->entity_internal_idx_max = 0;
> > +	media_graph_walk_cleanup(&mdev->pm_count_walk);
> > +	mutex_destroy(&mdev->graph_mutex);
> > +	mutex_destroy(&mdev->req_queue_mutex);
> > +}
> > +
> > +static void media_device_release(struct media_devnode *devnode)
> > +{
> > +	struct media_device *mdev = to_media_device(devnode);
> > +
> > +	if (mdev->ops && mdev->ops->release) {
> > +		/*
> > +		 * If release op isn't set, __media_device_release() is called
> > +		 * via media_device_cleanup().
> > +		 */
> > +		__media_device_release(mdev);
> > +		mdev->ops->release(mdev);
> > +	}
> > +}
> > +
> >  void media_device_init(struct media_device *mdev)
> >  {
> >  	INIT_LIST_HEAD(&mdev->entities);
> > @@ -712,6 +737,8 @@ void media_device_init(struct media_device *mdev)
> >  	mutex_init(&mdev->graph_mutex);
> >  	ida_init(&mdev->entity_internal_idx);
> >  	atomic_set(&mdev->request_id, 0);
> > +
> > +	mdev->devnode.release = media_device_release;
> >  	media_devnode_init(&mdev->devnode);
> >  
> >  	if (!*mdev->bus_info)
> > @@ -724,12 +751,9 @@ EXPORT_SYMBOL_GPL(media_device_init);
> >  
> >  void media_device_cleanup(struct media_device *mdev)
> >  {
> > -	ida_destroy(&mdev->entity_internal_idx);
> > -	mdev->entity_internal_idx_max = 0;
> > -	media_graph_walk_cleanup(&mdev->pm_count_walk);
> > -	mutex_destroy(&mdev->graph_mutex);
> > -	mutex_destroy(&mdev->req_queue_mutex);
> > -	put_device(&mdev->devnode.dev);
> > +	WARN_ON(mdev->ops && mdev->ops->release);
> > +	__media_device_release(mdev);
> > +	media_device_put(mdev);
> >  }
> >  EXPORT_SYMBOL_GPL(media_device_cleanup);
> >  
> > diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c
> > index 5057c48f8870..4ea05e42dafb 100644
> > --- a/drivers/media/mc/mc-devnode.c
> > +++ b/drivers/media/mc/mc-devnode.c
> > @@ -59,6 +59,10 @@ static void media_devnode_release(struct device *cd)
> >  {
> >  	struct media_devnode *devnode = to_media_devnode(cd);
> >  
> > +	/* If the devnode has a ref, it is simply released by the user. */
> > +	if (devnode->ref)
> 
> The structure has no ref member.

This could explain some of the problems GCC has had compiling this patch.
;-)

This belongs to patch "media: mc: Implement best effort media device
removal safety sans refcount".

> 
> > +		return;
> > +
> >  	if (devnode->minor != -1)
> >  		media_devnode_free_minor(devnode->minor);
> >  
> > @@ -213,6 +217,7 @@ static const struct file_operations media_devnode_fops = {
> >  void media_devnode_init(struct media_devnode *devnode)
> >  {
> >  	device_initialize(&devnode->dev);
> > +	devnode->dev.release = media_devnode_release;
> >  	devnode->minor = -1;
> >  }
> >  
> > @@ -246,7 +251,6 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
> >  
> >  	devnode->dev.bus = &media_bus_type;
> >  	devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
> > -	devnode->dev.release = media_devnode_release;
> >  	if (devnode->parent)
> >  		devnode->dev.parent = devnode->parent;
> >  	dev_set_name(&devnode->dev, "media%d", devnode->minor);
> > diff --git a/include/media/media-device.h b/include/media/media-device.h
> > index fb0855b217ce..c6816be0eee8 100644
> > --- a/include/media/media-device.h
> > +++ b/include/media/media-device.h
> > @@ -62,6 +62,7 @@ struct media_entity_notify {
> >   *	       request (and thus the buffer) must be available to the driver.
> >   *	       And once a buffer is queued, then the driver can complete
> >   *	       or delete objects from the request before req_queue exits.
> > + * @release: Release the resources of the media device.
> >   */
> >  struct media_device_ops {
> >  	int (*link_notify)(struct media_link *link, u32 flags,
> > @@ -70,6 +71,7 @@ struct media_device_ops {
> >  	void (*req_free)(struct media_request *req);
> >  	int (*req_validate)(struct media_request *req);
> >  	void (*req_queue)(struct media_request *req);
> > +	void (*release)(struct media_device *mdev);
> >  };
> >  
> >  /**
> > @@ -219,6 +221,30 @@ struct usb_device;
> >   */
> >  void media_device_init(struct media_device *mdev);
> >  
> > +/**
> > + * media_device_get() - Get a reference to a media device
> 
> Maybe mimick the get_device() wording and state "atomically increment
> the reference count for the media device" ? Same for put.

Sounds good.

> 
> > + *
> > + * @mdev: media device
> 
> This should return a pointer to the media_device, as other get functions
> do.

Yes, I'll do that for v3.

> 
> > + */
> > +#define media_device_get(mdev)						\
> > +	do {								\
> > +		dev_dbg((mdev)->dev, "%s: get media device %s\n",	\
> > +			__func__, (mdev)->bus_info);			\
> 
> Do we really need this ? I'd prefer inline functions to ensure type
> safety. If we need to track the get/put callers, I think using ftrace
> would be a better option.

I think we can drop this. It was useful for development though.

> 
> > +		get_device(&(mdev)->devnode.dev);			\
> > +	} while (0)
> > +
> > +/**
> > + * media_device_put() - Put a reference to a media device
> > + *
> > + * @mdev: media device
> > + */
> > +#define media_device_put(mdev)						\
> > +	do {								\
> > +		dev_dbg((mdev)->dev, "%s: put media device %s\n",	\
> > +			__func__, (mdev)->bus_info);			\
> > +		put_device(&(mdev)->devnode.dev);			\
> > +	} while (0)
> > +
> >  /**
> >   * media_device_cleanup() - Cleanups a media device element
> >   *
> > @@ -432,6 +458,8 @@ void __media_device_usb_init(struct media_device *mdev,
> >  			     const char *driver_name);
> >  
> >  #else
> > +#define media_device_get(mdev) do { } while (0)
> > +#define media_device_put(mdev) do { } while (0)
> >  static inline int media_device_register(struct media_device *mdev)
> >  {
> >  	return 0;
> 

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 00/29] Media device lifetime management
  2024-02-07 10:55 ` Laurent Pinchart
@ 2024-03-07 10:57   ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-03-07 10:57 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil

On Wed, Feb 07, 2024 at 12:55:00PM +0200, Laurent Pinchart wrote:
> Hi Sakari,
> 
> I've just noticed that I send multiple Reviewed-by tags for this series
> using Laurent Pinchart <laurent.pinchart@ideasonboard.com> when I meant
> Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>. Could you
> replace the e-mail address when picking up the tags ?Sorry about the
> inconvenience.

No problem. I'll do it for v3.

-- 
Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v2 22/29] media: ipu3-cio2: Release the cio2 device context by media device callback
  2024-02-07 14:33   ` Laurent Pinchart
@ 2024-03-07 12:23     ` Sakari Ailus
  0 siblings, 0 replies; 92+ messages in thread
From: Sakari Ailus @ 2024-03-07 12:23 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil

Hi Laurent,

On Wed, Feb 07, 2024 at 04:33:50PM +0200, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.
> 
> On Wed, Dec 20, 2023 at 12:37:06PM +0200, Sakari Ailus wrote:
> > Use the media device release callback to release the cio2 device's data
> > structure. This approach has the benefit of not releasing memory which may
> > still be accessed through open file handles whilst the ipu3-cio2 driver is
> > being unbound.
> > 
> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> > ---
> >  drivers/media/pci/intel/ipu3/ipu3-cio2.c | 58 ++++++++++++++++--------
> >  1 file changed, 40 insertions(+), 18 deletions(-)
> > 
> > diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> > index 3222ec5b8345..bff66e6d3b1e 100644
> > --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> > +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
> > @@ -238,9 +238,15 @@ static int cio2_fbpt_init(struct cio2_device *cio2, struct cio2_queue *q)
> >  	return 0;
> >  }
> >  
> > -static void cio2_fbpt_exit(struct cio2_queue *q, struct device *dev)
> > +static int cio2_fbpt_exit(struct cio2_queue *q, struct device *dev)
> >  {
> > +	if (!q->fbpt)
> > +		return -ENOENT;
> > +
> >  	dma_free_coherent(dev, CIO2_FBPT_SIZE, q->fbpt, q->fbpt_bus_addr);
> > +	q->fbpt = NULL;
> > +
> > +	return 0;
> >  }
> >  
> >  /**************** CSI2 hardware setup ****************/
> > @@ -1643,13 +1649,13 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q)
> >  
> >  static void cio2_queue_exit(struct cio2_device *cio2, struct cio2_queue *q)
> >  {
> > -	vb2_video_unregister_device(&q->vdev);
> >  	media_entity_cleanup(&q->vdev.entity);
> >  	v4l2_device_unregister_subdev(&q->subdev);
> 
> Is the release callback the right time for this ?

Neither driver remove or media device release is entirely correct as we
don't have refcounting for these yet. A better place would still be in the
remove callback. I'll move it there.

> 
> >  	media_entity_cleanup(&q->subdev.entity);
> > -	cio2_fbpt_exit(q, &cio2->pci_dev->dev);
> > -	mutex_destroy(&q->subdev_lock);
> > -	mutex_destroy(&q->lock);
> > +	if (!cio2_fbpt_exit(q, &cio2->pci_dev->dev)) {
> 
> This doesn't look very nice, but I suppose there are many other things
> to clean up in this driver, so I'll close my eyes.

I'd say ipu3-cio2 is one of the better CSI-2 receiver drivers.

This is related to error handing: you can't call mutex_destroy() on a mutex
that's been already destroyed. cio2_queue_exit() is called when the media
device is released but we don't know here whether mutexes have been
initialised yet. I guess that's something that could be changed but it
would create more lines of code elsewhere.

> 
> > +		mutex_destroy(&q->subdev_lock);
> > +		mutex_destroy(&q->lock);
> > +	}
> >  }
> >  
> >  static int cio2_queues_init(struct cio2_device *cio2)
> > @@ -1695,6 +1701,23 @@ static int cio2_check_fwnode_graph(struct fwnode_handle *fwnode)
> >  	return cio2_check_fwnode_graph(fwnode->secondary);
> >  }
> >  
> > +static void cio2_media_release(struct media_device *mdev)
> > +{
> > +	struct cio2_device *cio2 =
> > +		container_of(mdev, struct cio2_device, media_dev);
> > +
> > +	v4l2_async_nf_cleanup(&cio2->notifier);
> > +	cio2_queues_exit(cio2);
> > +	cio2_fbpt_exit_dummy(cio2);
> > +	mutex_destroy(&cio2->lock);
> > +
> > +	kfree(cio2);
> > +}
> > +
> > +static const struct media_device_ops cio2_mdev_ops = {
> > +	.release = cio2_media_release,
> > +};
> > +
> >  /**************** PCI interface ****************/
> >  
> >  static int cio2_pci_probe(struct pci_dev *pci_dev,
> > @@ -1722,7 +1745,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
> >  			return r;
> >  	}
> >  
> > -	cio2 = devm_kzalloc(dev, sizeof(*cio2), GFP_KERNEL);
> > +	cio2 = kzalloc(sizeof(*cio2), GFP_KERNEL);
> >  	if (!cio2)
> >  		return -ENOMEM;
> >  	cio2->pci_dev = pci_dev;
> > @@ -1767,6 +1790,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
> >  	mutex_init(&cio2->lock);
> >  
> >  	cio2->media_dev.dev = dev;
> > +	cio2->media_dev.ops = &cio2_mdev_ops;
> >  	strscpy(cio2->media_dev.model, CIO2_DEVICE_NAME,
> >  		sizeof(cio2->media_dev.model));
> >  	cio2->media_dev.hw_revision = 0;
> > @@ -1774,7 +1798,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
> >  	media_device_init(&cio2->media_dev);
> >  	r = media_device_register(&cio2->media_dev);
> >  	if (r < 0)
> > -		goto fail_mutex_destroy;
> > +		goto fail_media_device_put;
> >  
> >  	cio2->v4l2_dev.mdev = &cio2->media_dev;
> >  	r = v4l2_device_register(dev, &cio2->v4l2_dev);
> > @@ -1808,35 +1832,33 @@ static int cio2_pci_probe(struct pci_dev *pci_dev,
> >  
> >  fail_clean_notifier:
> >  	v4l2_async_nf_unregister(&cio2->notifier);
> > -	v4l2_async_nf_cleanup(&cio2->notifier);
> > -	cio2_queues_exit(cio2);
> > +
> >  fail_v4l2_device_unregister:
> >  	v4l2_device_unregister(&cio2->v4l2_dev);
> > +
> >  fail_media_device_unregister:
> >  	media_device_unregister(&cio2->media_dev);
> > -	media_device_cleanup(&cio2->media_dev);
> > -fail_mutex_destroy:
> > -	mutex_destroy(&cio2->lock);
> > -	cio2_fbpt_exit_dummy(cio2);
> >  
> > +fail_media_device_put:
> > +	media_device_put(&cio2->media_dev);
> >  	return r;
> >  }
> >  
> >  static void cio2_pci_remove(struct pci_dev *pci_dev)
> >  {
> >  	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
> > +	unsigned int i;
> >  
> >  	media_device_unregister(&cio2->media_dev);
> > +	for (i = 0; i < CIO2_QUEUES; i++)
> > +		vb2_video_unregister_device(&cio2->queue[i].vdev);
> >  	v4l2_device_unregister(&cio2->v4l2_dev);
> >  	v4l2_async_nf_unregister(&cio2->notifier);
> > -	v4l2_async_nf_cleanup(&cio2->notifier);
> > -	cio2_queues_exit(cio2);
> > -	cio2_fbpt_exit_dummy(cio2);
> > -	media_device_cleanup(&cio2->media_dev);
> > -	mutex_destroy(&cio2->lock);
> >  
> >  	pm_runtime_forbid(&pci_dev->dev);
> >  	pm_runtime_get_noresume(&pci_dev->dev);
> > +
> > +	media_device_put(&cio2->media_dev);
> >  }
> >  
> >  static int __maybe_unused cio2_runtime_suspend(struct device *dev)
> 

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 92+ messages in thread

end of thread, other threads:[~2024-03-07 12:23 UTC | newest]

Thread overview: 92+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-20 10:36 [PATCH v2 00/29] Media device lifetime management Sakari Ailus
2023-12-20 10:36 ` [PATCH v2 01/29] Revert "[media] media: fix media devnode ioctl/syscall and unregister race" Sakari Ailus
2023-12-20 10:36 ` [PATCH v2 02/29] Revert "media: utilize new cdev_device_add helper function" Sakari Ailus
2023-12-20 10:36 ` [PATCH v2 03/29] Revert "[media] media: fix use-after-free in cdev_put() when app exits after driver unbind" Sakari Ailus
2023-12-20 10:36 ` [PATCH v2 04/29] media: mc: utilize new cdev_device_add helper function Sakari Ailus
2024-02-07  9:38   ` Laurent Pinchart
2024-02-07  9:51     ` Laurent Pinchart
2024-02-21 12:55       ` Sakari Ailus
2023-12-20 10:36 ` [PATCH v2 05/29] Revert "media: uvcvideo: Refactor teardown of uvc on USB disconnect" Sakari Ailus
2023-12-20 10:36 ` [PATCH v2 06/29] Revert "[media] media-device: dynamically allocate struct media_devnode" Sakari Ailus
2023-12-20 10:36 ` [PATCH v2 07/29] media: uvcvideo: Refactor teardown of uvc on USB disconnect Sakari Ailus
2023-12-20 10:36 ` [PATCH v2 08/29] media: mc: Drop nop release callback Sakari Ailus
2024-02-07  9:55   ` Laurent Pinchart
2023-12-20 10:36 ` [PATCH v2 09/29] media: mc: Do not call cdev_device_del() if cdev_device_add() fails Sakari Ailus
2024-02-07  9:57   ` Laurent Pinchart
2024-03-05  8:13     ` Sakari Ailus
2023-12-20 10:36 ` [PATCH v2 10/29] media: mc: Delete character device early Sakari Ailus
2024-02-07 10:08   ` Laurent Pinchart
2024-03-05  8:52     ` Sakari Ailus
2023-12-20 10:36 ` [PATCH v2 11/29] media: mc: Split initialising and adding media devnode Sakari Ailus
2024-02-07 10:46   ` Laurent Pinchart
2024-03-05  8:59     ` Sakari Ailus
2023-12-20 10:36 ` [PATCH v2 12/29] media: mc: Shuffle functions around Sakari Ailus
2024-02-07 10:47   ` Laurent Pinchart
2023-12-20 10:36 ` [PATCH v2 13/29] media: mc: Initialise media devnode in media_device_init() Sakari Ailus
2024-02-07 10:51   ` Laurent Pinchart
2023-12-20 10:36 ` [PATCH v2 14/29] media: mc: Refactor media devnode minor clearing Sakari Ailus
2024-02-05 14:46   ` Hans Verkuil
2024-02-07 10:53   ` Laurent Pinchart
2023-12-20 10:36 ` [PATCH v2 15/29] media: mc: Unassign minor only if it has been assigned Sakari Ailus
2024-02-05 14:48   ` Hans Verkuil
2024-02-07 10:58   ` Laurent Pinchart
2024-02-21  9:24     ` Sakari Ailus
2023-12-20 10:37 ` [PATCH v2 16/29] media: mc: Refcount the media device Sakari Ailus
2024-02-07 11:08   ` Laurent Pinchart
2024-03-07 10:37     ` Sakari Ailus
2023-12-20 10:37 ` [PATCH v2 17/29] media: v4l: Acquire a reference to the media device for every video device Sakari Ailus
2024-02-05 14:56   ` Hans Verkuil
2024-02-07 11:13     ` Laurent Pinchart
2024-02-21 10:43       ` Sakari Ailus
2024-02-21 12:19         ` Laurent Pinchart
2024-02-21 12:35           ` Sakari Ailus
2024-02-21 10:40     ` Sakari Ailus
2024-02-21 10:51       ` Hans Verkuil
2024-02-21 11:44         ` Sakari Ailus
2024-03-05  7:43           ` Sakari Ailus
2024-03-05  7:46             ` Hans Verkuil
2023-12-20 10:37 ` [PATCH v2 18/29] media: mc: Postpone graph object removal until free Sakari Ailus
2024-02-07 14:18   ` Laurent Pinchart
2023-12-20 10:37 ` [PATCH v2 19/29] media: omap3isp: Release the isp device struct by media device callback Sakari Ailus
2024-02-07 14:23   ` Laurent Pinchart
2023-12-20 10:37 ` [PATCH v2 20/29] media: ipu3-cio2: Call v4l2_device_unregister() earlier Sakari Ailus
2024-02-07 14:24   ` Laurent Pinchart
2024-03-05 10:21     ` Sakari Ailus
2024-03-05 10:22       ` Sakari Ailus
2023-12-20 10:37 ` [PATCH v2 21/29] media: ipu3-cio2: Request IRQ earlier Sakari Ailus
2024-02-05 14:58   ` Hans Verkuil
2024-02-07 14:34     ` Laurent Pinchart
2024-02-21 10:51       ` Sakari Ailus
2023-12-20 10:37 ` [PATCH v2 22/29] media: ipu3-cio2: Release the cio2 device context by media device callback Sakari Ailus
2024-02-07 14:33   ` Laurent Pinchart
2024-03-07 12:23     ` Sakari Ailus
2023-12-20 10:37 ` [PATCH v2 23/29] media: vimc: Release resources on media device release Sakari Ailus
2024-02-05 15:02   ` Hans Verkuil
2024-02-07 14:38     ` Laurent Pinchart
2024-02-21 10:55       ` Sakari Ailus
2024-02-21 10:53     ` Sakari Ailus
2024-02-21 11:02       ` Laurent Pinchart
2024-02-21 11:38         ` Sakari Ailus
2024-02-21 11:19       ` Hans Verkuil
2024-02-21 11:40         ` Sakari Ailus
2024-02-21 11:48           ` Hans Verkuil
2024-02-21 12:02             ` Sakari Ailus
2023-12-20 10:37 ` [PATCH v2 24/29] media: Documentation: Document how Media device resources are released Sakari Ailus
2024-02-05 15:04   ` Hans Verkuil
2024-02-07 14:43   ` Laurent Pinchart
2024-02-21 11:37     ` Sakari Ailus
2023-12-20 10:37 ` [PATCH v2 25/29] media: mc: Add per-file-handle data support Sakari Ailus
2024-02-05 15:08   ` Hans Verkuil
2023-12-20 10:37 ` [PATCH v2 26/29] media: mc: Maintain a list of open file handles in a media device Sakari Ailus
2024-02-05 15:11   ` Hans Verkuil
2024-02-05 15:16     ` Laurent Pinchart
2024-02-05 15:32       ` Hans Verkuil
2024-02-05 15:41         ` Laurent Pinchart
2024-02-21 11:53           ` Sakari Ailus
2023-12-20 10:37 ` [PATCH v2 27/29] media: mc: Implement best effort media device removal safety sans refcount Sakari Ailus
2023-12-20 10:37 ` [PATCH v2 28/29] media: mc: Warn about drivers not releasing media device safely Sakari Ailus
2023-12-20 10:37 ` [PATCH v2 29/29] media: Documentation: Document media device memory safety helper Sakari Ailus
2023-12-20 10:52 ` [PATCH v2 00/29] Media device lifetime management Laurent Pinchart
2023-12-20 11:30   ` Sakari Ailus
2024-02-07 10:55 ` Laurent Pinchart
2024-03-07 10:57   ` Sakari Ailus

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).