aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--Documentation/gpu/drm-internals.rst6
-rw-r--r--drivers/gpu/drm/drm_drv.c18
-rw-r--r--drivers/gpu/drm/drm_managed.c53
-rw-r--r--include/drm/drm_drv.h4
-rw-r--r--include/drm/drm_managed.h55
5 files changed, 133 insertions, 3 deletions
diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst
index a6b6145fda78..12272b168580 100644
--- a/Documentation/gpu/drm-internals.rst
+++ b/Documentation/gpu/drm-internals.rst
@@ -138,6 +138,12 @@ Managed Resources
.. kernel-doc:: drivers/gpu/drm/drm_managed.c
:doc: managed resources
+.. kernel-doc:: drivers/gpu/drm/drm_managed.c
+ :export:
+
+.. kernel-doc:: include/drm/drm_managed.h
+ :internal:
+
Bus-specific Device Registration and PCI Support
------------------------------------------------
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 158fea71371b..7dad7813fca1 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -259,9 +259,15 @@ void drm_minor_release(struct drm_minor *minor)
* any other resources allocated at device initialization and drop the driver's
* reference to &drm_device using drm_dev_put().
*
- * Note that the lifetime rules for &drm_device instance has still a lot of
- * historical baggage. Hence use the reference counting provided by
- * drm_dev_get() and drm_dev_put() only carefully.
+ * Note that any allocation or resource which is visible to userspace must be
+ * released only when the final drm_dev_put() is called, and not when the
+ * driver is unbound from the underlying physical struct &device. Best to use
+ * &drm_device managed resources with drmm_add_action(), drmm_kmalloc() and
+ * related functions.
+ *
+ * devres managed resources like devm_kmalloc() can only be used for resources
+ * directly related to the underlying hardware device, and only used in code
+ * paths fully protected by drm_dev_enter() and drm_dev_exit().
*
* Display driver example
* ~~~~~~~~~~~~~~~~~~~~~~
@@ -605,6 +611,9 @@ static void drm_dev_init_release(struct drm_device *dev, void *res)
* arbitrary offset, you must supply a &drm_driver.release callback and control
* the finalization explicitly.
*
+ * Note that drivers must call drmm_add_final_kfree() after this function has
+ * completed successfully.
+ *
* RETURNS:
* 0 on success, or error code on failure.
*/
@@ -706,6 +715,9 @@ static void devm_drm_dev_init_release(void *data)
* Managed drm_dev_init(). The DRM device initialized with this function is
* automatically put on driver detach using drm_dev_put().
*
+ * Note that drivers must call drmm_add_final_kfree() after this function has
+ * completed successfully.
+ *
* RETURNS:
* 0 on success, or error code on failure.
*/
diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c
index 7246a8318137..4955241ceb4c 100644
--- a/drivers/gpu/drm/drm_managed.c
+++ b/drivers/gpu/drm/drm_managed.c
@@ -20,7 +20,19 @@
* Inspired by struct &device managed resources, but tied to the lifetime of
* struct &drm_device, which can outlive the underlying physical device, usually
* when userspace has some open files and other handles to resources still open.
+ *
+ * Release actions can be added with drmm_add_action(), memory allocations can
+ * be done directly with drmm_kmalloc() and the related functions. Everything
+ * will be released on the final drm_dev_put() in reverse order of how the
+ * release actions have been added and memory has been allocated since driver
+ * loading started with drm_dev_init().
+ *
+ * Note that release actions and managed memory can also be added and removed
+ * during the lifetime of the driver, all the functions are fully concurrent
+ * safe. But it is recommended to use managed resources only for resources that
+ * change rarely, if ever, during the lifetime of the &drm_device instance.
*/
+
struct drmres_node {
struct list_head entry;
drmres_release_t release;
@@ -111,6 +123,18 @@ static void add_dr(struct drm_device *dev, struct drmres *dr)
dr, dr->node.name, (unsigned long) dr->node.size);
}
+/**
+ * drmm_add_final_kfree - add release action for the final kfree()
+ * @dev: DRM device
+ * @container: pointer to the kmalloc allocation containing @dev
+ *
+ * Since the allocation containing the struct &drm_device must be allocated
+ * before it can be initialized with drm_dev_init() there's no way to allocate
+ * that memory with drmm_kmalloc(). To side-step this chicken-egg problem the
+ * pointer for this final kfree() must be specified by calling this function. It
+ * will be released in the final drm_dev_put() for @dev, after all other release
+ * actions installed through drmm_add_action() have been processed.
+ */
void drmm_add_final_kfree(struct drm_device *dev, void *container)
{
WARN_ON(dev->managed.final_kfree);
@@ -163,6 +187,16 @@ int __drmm_add_action_or_reset(struct drm_device *dev,
}
EXPORT_SYMBOL(__drmm_add_action_or_reset);
+/**
+ * drmm_kmalloc - &drm_device managed kmalloc()
+ * @dev: DRM device
+ * @size: size of the memory allocation
+ * @gfp: GFP allocation flags
+ *
+ * This is a &drm_device managed version of kmalloc(). The allocated memory is
+ * automatically freed on the final drm_dev_put(). Memory can also be freed
+ * before the final drm_dev_put() by calling drmm_kfree().
+ */
void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp)
{
struct drmres *dr;
@@ -181,6 +215,16 @@ void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp)
}
EXPORT_SYMBOL(drmm_kmalloc);
+/**
+ * drmm_kstrdup - &drm_device managed kstrdup()
+ * @dev: DRM device
+ * @s: 0-terminated string to be duplicated
+ * @gfp: GFP allocation flags
+ *
+ * This is a &drm_device managed version of kstrdup(). The allocated memory is
+ * automatically freed on the final drm_dev_put() and works exactly like a
+ * memory allocation obtained by drmm_kmalloc().
+ */
char *drmm_kstrdup(struct drm_device *dev, const char *s, gfp_t gfp)
{
size_t size;
@@ -197,6 +241,15 @@ char *drmm_kstrdup(struct drm_device *dev, const char *s, gfp_t gfp)
}
EXPORT_SYMBOL_GPL(drmm_kstrdup);
+/**
+ * drmm_kfree - &drm_device managed kfree()
+ * @dev: DRM device
+ * @data: memory allocation to be freed
+ *
+ * This is a &drm_device managed version of kfree() which can be used to
+ * release memory allocated through drmm_kmalloc() or any of its related
+ * functions before the final drm_dev_put() of @dev.
+ */
void drmm_kfree(struct drm_device *dev, void *data)
{
struct drmres *dr_match = NULL, *dr;
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index b778f3642a90..e0ea577559ff 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -263,6 +263,10 @@ struct drm_driver {
*
* Optional callback for destroying device data after the final
* reference is released, i.e. the device is being destroyed.
+ *
+ * This is deprecated, clean up all memory allocations associated with a
+ * &drm_device using drmm_add_action(), drmm_kmalloc() and related
+ * managed resources functions.
*/
void (*release) (struct drm_device *);
diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h
index 191d8d206ff4..ca4114633bf9 100644
--- a/include/drm/drm_managed.h
+++ b/include/drm/drm_managed.h
@@ -11,6 +11,16 @@ struct drm_device;
typedef void (*drmres_release_t)(struct drm_device *dev, void *res);
+/**
+ * drmm_add_action - add a managed release action to a &drm_device
+ * @dev: DRM device
+ * @action: function which should be called when @dev is released
+ * @data: opaque pointer, passed to @action
+ *
+ * This function adds the @release action with optional parameter @data to the
+ * list of cleanup actions for @dev. The cleanup actions will be run in reverse
+ * order in the final drm_dev_put() call for @dev.
+ */
#define drmm_add_action(dev, action, data) \
__drmm_add_action(dev, action, data, #action)
@@ -18,6 +28,15 @@ int __must_check __drmm_add_action(struct drm_device *dev,
drmres_release_t action,
void *data, const char *name);
+/**
+ * drmm_add_action_or_reset - add a managed release action to a &drm_device
+ * @dev: DRM device
+ * @action: function which should be called when @dev is released
+ * @data: opaque pointer, passed to @action
+ *
+ * Similar to drmm_add_action(), with the only difference that upon failure
+ * @action is directly called for any cleanup work necessary on failures.
+ */
#define drmm_add_action_or_reset(dev, action, data) \
__drmm_add_action_or_reset(dev, action, data, #action)
@@ -28,10 +47,33 @@ int __must_check __drmm_add_action_or_reset(struct drm_device *dev,
void drmm_add_final_kfree(struct drm_device *dev, void *container);
void *drmm_kmalloc(struct drm_device *dev, size_t size, gfp_t gfp) __malloc;
+
+/**
+ * drmm_kzalloc - &drm_device managed kzalloc()
+ * @dev: DRM device
+ * @size: size of the memory allocation
+ * @gfp: GFP allocation flags
+ *
+ * This is a &drm_device managed version of kzalloc(). The allocated memory is
+ * automatically freed on the final drm_dev_put(). Memory can also be freed
+ * before the final drm_dev_put() by calling drmm_kfree().
+ */
static inline void *drmm_kzalloc(struct drm_device *dev, size_t size, gfp_t gfp)
{
return drmm_kmalloc(dev, size, gfp | __GFP_ZERO);
}
+
+/**
+ * drmm_kmalloc_array - &drm_device managed kmalloc_array()
+ * @dev: DRM device
+ * @n: number of array elements to allocate
+ * @size: size of array member
+ * @flags: GFP allocation flags
+ *
+ * This is a &drm_device managed version of kmalloc_array(). The allocated
+ * memory is automatically freed on the final drm_dev_put() and works exactly
+ * like a memory allocation obtained by drmm_kmalloc().
+ */
static inline void *drmm_kmalloc_array(struct drm_device *dev,
size_t n, size_t size, gfp_t flags)
{
@@ -42,11 +84,24 @@ static inline void *drmm_kmalloc_array(struct drm_device *dev,
return drmm_kmalloc(dev, bytes, flags);
}
+
+/**
+ * drmm_kcalloc - &drm_device managed kcalloc()
+ * @dev: DRM device
+ * @n: number of array elements to allocate
+ * @size: size of array member
+ * @flags: GFP allocation flags
+ *
+ * This is a &drm_device managed version of kcalloc(). The allocated memory is
+ * automatically freed on the final drm_dev_put() and works exactly like a
+ * memory allocation obtained by drmm_kmalloc().
+ */
static inline void *drmm_kcalloc(struct drm_device *dev,
size_t n, size_t size, gfp_t flags)
{
return drmm_kmalloc_array(dev, n, size, flags | __GFP_ZERO);
}
+
char *drmm_kstrdup(struct drm_device *dev, const char *s, gfp_t gfp);
void drmm_kfree(struct drm_device *dev, void *data);