Unverified Commit dcba396f authored by Anusha Srivatsa's avatar Anusha Srivatsa Committed by Maxime Ripard
Browse files

drm/panel: Add refcount support



Allocate panel via reference counting. Add _get() and _put() helper
functions to ensure panel allocations are refcounted. Avoid use after
free by ensuring panel pointer is valid and can be usable till the last
reference is put.

Reviewed-by: default avatarLuca Ceresoli <luca.ceresoli@bootlin.com>
Reviewed-by: default avatarMaxime Ripard <mripard@kernel.org>
Signed-off-by: default avatarAnusha Srivatsa <asrivats@redhat.com>
Link: https://lore.kernel.org/r/20250331-b4-panel-refcounting-v4-2-dad50c60c6c9@redhat.com


Signed-off-by: default avatarMaxime Ripard <mripard@kernel.org>
parent ed9c594d
Loading
Loading
Loading
Loading
+63 −1
Original line number Diff line number Diff line
@@ -355,24 +355,86 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np)
}
EXPORT_SYMBOL(of_drm_find_panel);

static void __drm_panel_free(struct kref *kref)
{
	struct drm_panel *panel = container_of(kref, struct drm_panel, refcount);

	kfree(panel->container);
}

/**
 * drm_panel_get - Acquire a panel reference
 * @panel: DRM panel
 *
 * This function increments the panel's refcount.
 * Returns:
 * Pointer to @panel
 */
struct drm_panel *drm_panel_get(struct drm_panel *panel)
{
	if (!panel)
		return panel;

	kref_get(&panel->refcount);

	return panel;
}
EXPORT_SYMBOL(drm_panel_get);

/**
 * drm_panel_put - Release a panel reference
 * @panel: DRM panel
 *
 * This function decrements the panel's reference count and frees the
 * object if the reference count drops to zero.
 */
void drm_panel_put(struct drm_panel *panel)
{
	if (panel)
		kref_put(&panel->refcount, __drm_panel_free);
}
EXPORT_SYMBOL(drm_panel_put);

/**
 * drm_panel_put_void - wrapper to drm_panel_put() taking a void pointer
 *
 * @data: pointer to @struct drm_panel, cast to a void pointer
 *
 * Wrapper of drm_panel_put() to be used when a function taking a void
 * pointer is needed, for example as a devm action.
 */
static void drm_panel_put_void(void *data)
{
	struct drm_panel *panel = (struct drm_panel *)data;

	drm_panel_put(panel);
}

void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset,
			     const struct drm_panel_funcs *funcs,
			     int connector_type)
{
	void *container;
	struct drm_panel *panel;
	int err;

	if (!funcs) {
		dev_warn(dev, "Missing funcs pointer\n");
		return ERR_PTR(-EINVAL);
	}

	container = devm_kzalloc(dev, size, GFP_KERNEL);
	container = kzalloc(size, GFP_KERNEL);
	if (!container)
		return ERR_PTR(-ENOMEM);

	panel = container + offset;
	panel->container = container;
	panel->funcs = funcs;
	kref_init(&panel->refcount);

	err = devm_add_action_or_reset(dev, drm_panel_put_void, panel);
	if (err)
		return ERR_PTR(err);

	drm_panel_init(panel, dev, funcs, connector_type);

+19 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/kref.h>

struct backlight_device;
struct dentry;
@@ -266,6 +267,17 @@ struct drm_panel {
	 * If true then the panel has been enabled.
	 */
	bool enabled;

	/**
	 * @container: Pointer to the private driver struct embedding this
	 * @struct drm_panel.
	 */
	void *container;

	/**
	 * @refcount: reference count of users referencing this panel.
	 */
	struct kref refcount;
};

void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset,
@@ -282,6 +294,10 @@ void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset,
 * @connector_type: the connector type (DRM_MODE_CONNECTOR_*) corresponding to
 * the panel interface
 *
 * The reference count of the returned panel is initialized to 1. This
 * reference will be automatically dropped via devm (by calling
 * drm_panel_put()) when @dev is removed.
 *
 * Returns:
 * Pointer to container structure embedding the panel, ERR_PTR on failure.
 */
@@ -294,6 +310,9 @@ void drm_panel_init(struct drm_panel *panel, struct device *dev,
		    const struct drm_panel_funcs *funcs,
		    int connector_type);

struct drm_panel *drm_panel_get(struct drm_panel *panel);
void drm_panel_put(struct drm_panel *panel);

void drm_panel_add(struct drm_panel *panel);
void drm_panel_remove(struct drm_panel *panel);