Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




Den 21.01.2019 10.55, skrev Daniel Vetter:
> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
>>> This adds resource managed (devres) versions of drm_dev_init() and
>>> drm_dev_register().
>>>
>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
>>> fbdev emulation as well.
>>>
>>> devm_drm_dev_register() isn't exported since there are no users.
>>>
>>> Signed-off-by: Noralf Trønnes <noralf@xxxxxxxxxxx>
>>

<snip>

>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
>>> index 381581b01d48..12129772be45 100644
>>> --- a/drivers/gpu/drm/drm_drv.c
>>> +++ b/drivers/gpu/drm/drm_drv.c
>>> @@ -36,6 +36,7 @@
>>>  
>>>  #include <drm/drm_client.h>
>>>  #include <drm/drm_drv.h>
>>> +#include <drm/drm_fb_helper.h>
>>>  #include <drm/drmP.h>
>>>  
>>>  #include "drm_crtc_internal.h"
>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
>>>  }
>>>  EXPORT_SYMBOL(drm_dev_unregister);
>>>  
>>> +static void devm_drm_dev_init_release(void *data)
>>> +{
>>> +	drm_dev_put(data);
> 
> We need drm_dev_unplug() here, or this isn't safe.

This function is only used to cover the error path if probe fails before
devm_drm_dev_register() is called. devm_drm_dev_register_release() is
the one that calls unplug. There are comments about this in the functions.

> 
>>> +}
>>> +
>>> +/**
>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
>>> + * @parent: Parent device object
>>> + * @dev: DRM device
>>> + * @driver: DRM driver
>>> + *
>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
>>> + * automatically released on driver detach. You must supply a
> 
> I think a bit more clarity here would be good:
> 
> "... automatically released on driver unbind by callind drm_dev_unplug()."
> 
>>> + * &drm_driver.release callback to control the finalization explicitly.
> 
> I think a loud warning for these is in order:
> 
> "WARNING:
> 
> "In generally it is unsafe to use devm functions for drm structures
> because the lifetimes of &drm_device and the underlying &device do not
> match. This here works because it doesn't immediately free anything, but
> only calls drm_dev_unplug(), which internally decrements the &drm_device
> refcount through drm_dev_put().
> 
> "All other drm structures must still be explicitly released in the
> &drm_driver.release callback."
> 
> While thinking about this I just realized that with this design we have no
> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
> kinds of things will leak badly (connectors, fb, ...), but there's no
> place to call it:
> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
>   drm_dev_unregister in there must be called _before_ we start to shut
>   down anything.
> - drm_driver.release is way too late.
> 
> Ofc for a real hotunplug there's no point in shutting down the hw (it's
> already gone), but for a driver unload/unbind it would be nice if this
> happens automatically and in the right order.
> 
> So not sure what to do here really.

How about this change: (it breaks the rule of pulling helpers into the
core, so maybe we should put the devm_ functions into the simple KMS
helper instead?)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 12129772be45..7ed9550baff6 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -34,7 +34,9 @@
 #include <linux/slab.h>
 #include <linux/srcu.h>

+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_client.h>
+#include <drm/drm_crtc_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drmP.h>
@@ -355,17 +357,7 @@ void drm_dev_exit(int idx)
 }
 EXPORT_SYMBOL(drm_dev_exit);

-/**
- * drm_dev_unplug - unplug a DRM device
- * @dev: DRM device
- *
- * This unplugs a hotpluggable DRM device, which makes it inaccessible to
- * userspace operations. Entry-points can use drm_dev_enter() and
- * drm_dev_exit() to protect device resources in a race free manner. This
- * essentially unregisters the device like drm_dev_unregister(), but can be
- * called while there are still open users of @dev.
- */
-void drm_dev_unplug(struct drm_device *dev)
+static void __drm_dev_unplug(struct drm_device *dev, bool shutdown)
 {
        /*
         * After synchronizing any critical read section is guaranteed
to see
@@ -378,11 +370,32 @@ void drm_dev_unplug(struct drm_device *dev)

        drm_dev_unregister(dev);

+       if (shutdown)
+               drm_kms_helper_poll_fini(dev);
+
        mutex_lock(&drm_global_mutex);
-       if (dev->open_count == 0)
+       if (dev->open_count == 0) {
+               if (shutdown)
+                       drm_atomic_helper_shutdown(dev);
                drm_dev_put(dev);
+       }
        mutex_unlock(&drm_global_mutex);
 }
+
+/**
+ * drm_dev_unplug - unplug a DRM device
+ * @dev: DRM device
+ *
+ * This unplugs a hotpluggable DRM device, which makes it inaccessible to
+ * userspace operations. Entry-points can use drm_dev_enter() and
+ * drm_dev_exit() to protect device resources in a race free manner. This
+ * essentially unregisters the device like drm_dev_unregister(), but can be
+ * called while there are still open users of @dev.
+ */
+void drm_dev_unplug(struct drm_device *dev)
+{
+       __drm_dev_unplug(dev, false);
+}
 EXPORT_SYMBOL(drm_dev_unplug);

 /*
@@ -920,7 +933,7 @@ EXPORT_SYMBOL(devm_drm_dev_init);

 static void devm_drm_dev_register_release(void *data)
 {
-       drm_dev_unplug(data);
+       __drm_dev_unplug(data, true);
 }

 static int devm_drm_dev_register(struct drm_device *dev)


I realised that we should take a ref on the parent device so it can be
accessed by the DRM_DEV_ functions after unplug.


Noralf.


> 
>>> + *
>>> + * Note: This function must be used together with
>>> + * devm_drm_dev_register_with_fbdev().
>>> + *
>>> + * RETURNS:
>>> + * 0 on success, or error code on failure.
>>> + */
>>> +int devm_drm_dev_init(struct device *parent,
>>> +		      struct drm_device *dev,
>>> +		      struct drm_driver *driver)
>>> +{
>>> +	int ret;
>>> +
>>> +	if (WARN_ON(!parent || !driver->release))
>>> +		return -EINVAL;
>>> +
>>> +	ret = drm_dev_init(dev, driver, parent);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	/*
>>> +	 * This is a temporary release action that is used if probing fails
>>> +	 * before devm_drm_dev_register() is called.
>>> +	 */
>>> +	ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
>>> +	if (ret)
>>> +		devm_drm_dev_init_release(dev);
>>> +
>>> +	return ret;
>>> +}
>>> +EXPORT_SYMBOL(devm_drm_dev_init);
>>> +
>>> +static void devm_drm_dev_register_release(void *data)
>>> +{
>>> +	drm_dev_unplug(data);
>>> +}
>>> +
>>> +static int devm_drm_dev_register(struct drm_device *dev)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = drm_dev_register(dev, 0);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	/*
>>> +	 * This has now served it's purpose, remove it to not mess up ref
>>> +	 * counting.
>>> +	 */
>>> +	devm_remove_action(dev->dev, devm_drm_dev_init_release, dev);
>>> +
>>> +	ret = devm_add_action(dev->dev, devm_drm_dev_register_release, dev);
>>> +	if (ret)
>>> +		devm_drm_dev_register_release(dev);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * devm_drm_dev_register_with_fbdev - Resource managed drm_dev_register()
>>> + *                                    including generic fbdev emulation
>>> + * @dev: DRM device to register
>>> + * @fbdev_bpp: Preferred bits per pixel for fbdev (optional)
>>> + *
>>> + * Managed drm_dev_register() that also calls drm_fbdev_generic_setup().
>>> + * The DRM device registered with this function is automatically unregistered on
>>> + * driver detach using drm_dev_unplug().
>>> + *
>>> + * Note: This function must be used together with devm_drm_dev_init().
>>> + *
>>> + * For testing driver detach can be triggered manually by writing to the driver
>>> + * 'unbind' file.
>>> + *
>>> + * RETURNS:
>>> + * 0 on success, negative error code on failure.
>>> + */
>>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
>>> +				     unsigned int fbdev_bpp)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = devm_drm_dev_register(dev);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	drm_fbdev_generic_setup(dev, fbdev_bpp);
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL(devm_drm_dev_register_with_fbdev);
>>> +
>>>  /**
>>>   * drm_dev_set_unique - Set the unique name of a DRM device
>>>   * @dev: device of which to set the unique name
>>> diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
>>> index 35af23f5fa0d..c3f0477f2e7f 100644
>>> --- a/include/drm/drm_drv.h
>>> +++ b/include/drm/drm_drv.h
>>> @@ -628,6 +628,12 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
>>>  int drm_dev_register(struct drm_device *dev, unsigned long flags);
>>>  void drm_dev_unregister(struct drm_device *dev);
>>>  
>>> +int devm_drm_dev_init(struct device *parent,
>>> +		      struct drm_device *dev,
>>> +		      struct drm_driver *driver);
>>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
>>> +				     unsigned int fbdev_bpp);
>>> +
>>>  void drm_dev_get(struct drm_device *dev);
>>>  void drm_dev_put(struct drm_device *dev);
>>>  void drm_put_dev(struct drm_device *dev);
>>> -- 
>>> 2.20.1
>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@xxxxxxxxxxxxxxxxxxxxx
>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
>> -- 
>> Daniel Vetter
>> Software Engineer, Intel Corporation
>> http://blog.ffwll.ch
> 
_______________________________________________
dri-devel mailing list
dri-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/dri-devel




[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux