From: Jani Nikula <jani.nikula@linux.intel.com>
To: Luca Ceresoli <luca.ceresoli@bootlin.com>,
Simona Vetter <simona@ffwll.ch>, Inki Dae <inki.dae@samsung.com>,
Jagan Teki <jagan@amarulasolutions.com>,
Marek Szyprowski <m.szyprowski@samsung.com>,
Catalin Marinas <catalin.marinas@arm.com>,
Will Deacon <will@kernel.org>, Shawn Guo <shawnguo@kernel.org>,
Sascha Hauer <s.hauer@pengutronix.de>,
Pengutronix Kernel Team <kernel@pengutronix.de>,
Fabio Estevam <festevam@gmail.com>,
Daniel Thompson <danielt@kernel.org>,
Andrzej Hajda <andrzej.hajda@intel.com>,
Jonathan Corbet <corbet@lwn.net>
Cc: "Paul Kocialkowski" <contact@paulk.fr>,
"Maxime Ripard" <mripard@kernel.org>,
"Dmitry Baryshkov" <dmitry.baryshkov@linaro.org>,
"Neil Armstrong" <neil.armstrong@linaro.org>,
"Robert Foss" <rfoss@kernel.org>,
"Laurent Pinchart" <Laurent.pinchart@ideasonboard.com>,
"Jonas Karlman" <jonas@kwiboo.se>,
"Jernej Skrabec" <jernej.skrabec@gmail.com>,
"Maarten Lankhorst" <maarten.lankhorst@linux.intel.com>,
"Thomas Zimmermann" <tzimmermann@suse.de>,
"David Airlie" <airlied@gmail.com>,
"Hervé Codina" <herve.codina@bootlin.com>,
"Thomas Petazzoni" <thomas.petazzoni@bootlin.com>,
linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org,
linux-doc@vger.kernel.org,
"Paul Kocialkowski" <paul.kocialkowski@bootlin.com>,
"Luca Ceresoli" <luca.ceresoli@bootlin.com>
Subject: Re: [PATCH v5 03/10] drm/bridge: add support for refcounted DRM bridges
Date: Tue, 31 Dec 2024 13:11:31 +0200 [thread overview]
Message-ID: <87seq4nm3g.fsf@intel.com> (raw)
In-Reply-To: <20241231-hotplug-drm-bridge-v5-3-173065a1ece1@bootlin.com>
On Tue, 31 Dec 2024, Luca Ceresoli <luca.ceresoli@bootlin.com> wrote:
> DRM bridges are currently considered as a fixed element of a DRM card, and
> thus their lifetime is assumed to extend for as long as the card
> exists. New use cases, such as hot-pluggable hardware with video bridges,
> require DRM bridges to be added and removed to a DRM card without tearing
> the card down. This is possible for connectors already (used by DP MST), so
> add this possibility to DRM bridges as well.
>
> Implementation is based on drm_connector_init() as far as it makes sense,
> and differs when it doesn't. A difference is that bridges are not exposed
> to userspace,hence struct drm_bridge does not embed a struct
> drm_mode_object which would provide the refcount and the free_cb. So here
> we add to struct drm_bridge just the refcount and free_cb fields (we don't
> need other struct drm_mode_object fields here) and instead of using the
> drm_mode_object_*() functions we reimplement from those functions the few
> lines that drm_bridge needs for refcounting.
>
> The function to enroll a private bridge driver data structure into
> refcounting is based on drm_connector_init() and so called
> drm_bridge_init() for symmetry, even though it does not initialize anything
> except the refcounting and the funcs pointer which is needed to access
> funcs->destroy.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> ---
>
> This patch was added in v5.
> ---
> drivers/gpu/drm/drm_bridge.c | 87 ++++++++++++++++++++++++++++++++++++
> include/drm/drm_bridge.h | 102 +++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 189 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index b1f0d25d55e23000521ac2ac37ee410348978ed4..6255ef59f73d8041a8cb7f2c6e23e5a67d1ae926 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -198,6 +198,85 @@
> static DEFINE_MUTEX(bridge_lock);
> static LIST_HEAD(bridge_list);
>
> +static void drm_bridge_put_void(void *data)
> +{
> + struct drm_bridge *bridge = (struct drm_bridge *)data;
> +
> + drm_bridge_put(bridge);
> +}
> +
> +static void drm_bridge_free(struct kref *kref)
> +{
> + struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount);
> +
> + DRM_DEBUG("bridge=%p\n", bridge);
> +
> + WARN_ON(!bridge->funcs->destroy);
Please don't add new DRM_DEBUG or WARN_ON where you can use the
drm_dbg_* or drm_WARN_ON variants.
> +
> + if (bridge->funcs->destroy)
> + bridge->funcs->destroy(bridge);
> +}
> +
> +/**
> + * drm_bridge_init - Initialize bridge and move private driver data
> + * lifetime management to the DRM bridge core
> + *
> + * @dev: struct device of the device whose removal shall trigger deallocation
> + * @bridge: the bridge to initialize
> + * @funcs: funcs structure for @bridge, which must have a valid .destroy func
> + *
> + * Takes over lifetime of a private bridge driver struct which embeds a
> + * struct drm_bridge. To be called by bridge drivers just after having
> + * allocated such a private structure. Initializes refcount to 1 and
> + * installs a callback which will call funcs->destroy when refcount drops
> + * to zero.
> + *
> + * After calling this function a bridge becomes a bridge with dynamic
> + * lifetime (aka a refcounted bridge).
> + *
> + * Drivers calling this function:
> + * - must not allocate the private structure using devm_*() functions
> + * - must not deallocate the private structure on device removal
> + * - must deallocate the private structure in funcs->destroy
> + *
> + * Drivers not calling this function:
> + * - must take care of freeing their private structure either by allocating
> + * it using devm_*() functions or free it explicitly on device removal
> + * using kfree()
> + * - must set funcs->destroy to NULL
> + *
> + * On failure, calls funcs->destroy, thus the caller does not need to free
> + * the driver private struct in case of error.
> + *
> + * Returns:
> + * Zero on success, error code on failure.
> + */
> +int drm_bridge_init(struct device *dev,
> + struct drm_bridge *bridge,
> + const struct drm_bridge_funcs *funcs)
> +{
> + int err;
> +
> + DRM_DEBUG("bridge=%p, funcs=%ps\n", bridge, funcs);
> +
> + if (!(funcs && funcs->destroy)) {
> + dev_warn(dev, "Missing funcs or destroy func pointer\n");
> + return -EINVAL;
> + }
> +
> + bridge->free_cb = drm_bridge_free;
> + kref_init(&bridge->refcount);
> +
> + err = devm_add_action_or_reset(dev, drm_bridge_put_void, bridge);
> + if (err)
> + return err;
> +
> + bridge->funcs = funcs;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_bridge_init);
> +
> /**
> * drm_bridge_add - add the given bridge to the global bridge list
> *
> @@ -207,6 +286,10 @@ void drm_bridge_add(struct drm_bridge *bridge)
> {
> struct drm_bridge *br, *tmp;
>
> + DRM_DEBUG("bridge=%p\n", bridge);
> +
> + drm_bridge_get(bridge);
> +
> mutex_init(&bridge->hpd_mutex);
>
> list_for_each_entry_safe(br, tmp, &bridge_list, list)
> @@ -251,6 +334,8 @@ void drm_bridge_remove(struct drm_bridge *bridge)
> {
> struct drm_bridge *br, *tmp;
>
> + DRM_DEBUG("bridge=%p\n", bridge);
> +
> mutex_lock(&bridge_lock);
> list_del_init(&bridge->list);
> mutex_unlock(&bridge_lock);
> @@ -260,6 +345,8 @@ void drm_bridge_remove(struct drm_bridge *bridge)
> br->funcs->bridge_event_notify(br, DRM_EVENT_BRIDGE_REMOVE, bridge);
>
> mutex_destroy(&bridge->hpd_mutex);
> +
> + drm_bridge_put(bridge);
> }
> EXPORT_SYMBOL(drm_bridge_remove);
>
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index 6976bd842cedf9ce06abfb7306e7a3b4915f0378..a548a6acb02e3d70c8e34de965f648320420a7d5 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -31,6 +31,7 @@
> #include <drm/drm_encoder.h>
> #include <drm/drm_mode_object.h>
> #include <drm/drm_modes.h>
> +#include <drm/drm_print.h>
>
> struct device_node;
>
> @@ -89,6 +90,18 @@ struct drm_bridge_funcs {
> */
> void (*detach)(struct drm_bridge *bridge);
>
> + /**
> + * @destroy:
> + *
> + * Clean up bridge resources for bridges with dynamic
> + * lifetime. This is called when the bridge refcount drops to
> + * zero. It is never called for bridges without dynamic lifetime.
> + *
> + * See drm_bridge_init() for info about bridges with dynamic
> + * lifetime.
> + */
> + void (*destroy)(struct drm_bridge *bridge);
> +
> /**
> * @mode_valid:
> *
> @@ -810,6 +823,18 @@ struct drm_bridge {
> const struct drm_bridge_timings *timings;
> /** @funcs: control functions */
> const struct drm_bridge_funcs *funcs;
> +
> + /**
> + * @refcount: reference count for bridges with dynamic lifetime
> + * (see drm_bridge_init)
> + */
> + struct kref refcount;
> + /**
> + * @free_cb: free function callback, only set for bridges with
> + * dynamic lifetime
> + */
> + void (*free_cb)(struct kref *kref);
> +
> /** @driver_private: pointer to the bridge driver's internal context */
> void *driver_private;
> /** @ops: bitmask of operations supported by the bridge */
> @@ -890,6 +915,83 @@ drm_priv_to_bridge(struct drm_private_obj *priv)
> return container_of(priv, struct drm_bridge, base);
> }
>
> +static inline bool drm_bridge_is_refcounted(struct drm_bridge *bridge)
> +{
> + return bridge->free_cb;
> +}
> +
> +/**
> + * drm_bridge_get - Acquire a bridge reference
> + * @bridge: DRM bridge
> + *
> + * This function increments the bridge's refcount.
> + *
> + * It does nothing on non-refcounted bridges. See drm_bridge_init().
> + */
> +static inline void drm_bridge_get(struct drm_bridge *bridge)
> +{
> + if (!drm_bridge_is_refcounted(bridge))
> + return;
> +
> + DRM_DEBUG("bridge=%p GET\n", bridge);
> +
> + kref_get(&bridge->refcount);
> +}
> +
> +/**
> + * drm_bridge_put - Release a bridge reference
> + * @bridge: DRM bridge
> + *
> + * This function decrements the bridge's reference count and frees the
> + * object if the reference count drops to zero.
> + *
> + * It does nothing on non-refcounted bridges. See drm_bridge_init().
> + *
> + * See also drm_bridge_put_and_clear() which is more handy in many cases.
> + */
> +static inline void drm_bridge_put(struct drm_bridge *bridge)
> +{
> + if (!drm_bridge_is_refcounted(bridge))
> + return;
> +
> + DRM_DEBUG("bridge=%p PUT\n", bridge);
> +
> + kref_put(&bridge->refcount, bridge->free_cb);
> +}
> +
> +/**
> + * drm_bridge_put_and_clear - Given a bridge pointer, clear the pointer
> + * then put the bridge
> + *
> + * @br_ptr: pointer to a struct drm_bridge (must be != NULL)
> + *
> + * Helper to put a DRM bridge (whose pointer is passed), but only after
> + * setting its pointer to NULL. Useful for drivers having struct drm_bridge
> + * pointers they need to dispose of, without leaving a use-after-free
> + * window where the pointed bridge might have been freed while still
> + * holding a pointer to it.
> + *
> + * For example a driver having this private struct::
> + *
> + * struct my_bridge {
> + * struct drm_bridge *remote_bridge;
> + * ...
> + * };
> + *
> + * can dispose of remote_bridge using::
> + *
> + * drm_bridge_put_and_clear(my_bridge->remote_bridge);
> + */
> +#define drm_bridge_put_and_clear(br_ptr) do { \
> + struct drm_bridge **brpp = &(br_ptr); \
> + struct drm_bridge *brp = *brpp; \
> + *brpp = NULL; \
> + drm_bridge_put(brp); \
> +} while (0)
> +
> +int drm_bridge_init(struct device *dev,
> + struct drm_bridge *bridge,
> + const struct drm_bridge_funcs *funcs);
> void drm_bridge_add(struct drm_bridge *bridge);
> int devm_drm_bridge_add(struct device *dev, struct drm_bridge *bridge);
> void drm_bridge_remove(struct drm_bridge *bridge);
--
Jani Nikula, Intel
next prev parent reply other threads:[~2024-12-31 11:11 UTC|newest]
Thread overview: 48+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-12-31 10:39 [PATCH v5 00/10] Add support for hot-pluggable DRM bridges Luca Ceresoli
2024-12-31 10:39 ` [PATCH v5 01/10] drm/bridge: allow bridges to be informed about added and removed bridges Luca Ceresoli
2024-12-31 10:39 ` [PATCH v5 02/10] drm/encoder: add drm_encoder_cleanup_from() Luca Ceresoli
2024-12-31 10:39 ` [PATCH v5 03/10] drm/bridge: add support for refcounted DRM bridges Luca Ceresoli
2024-12-31 11:11 ` Jani Nikula [this message]
2025-01-02 12:03 ` Luca Ceresoli
2025-01-03 9:36 ` Jani Nikula
2024-12-31 10:39 ` [PATCH v5 04/10] drm/bridge: add documentation of refcounted bridges Luca Ceresoli
2024-12-31 17:54 ` Randy Dunlap
2025-01-02 12:02 ` Luca Ceresoli
2025-01-06 10:39 ` Maxime Ripard
2025-01-06 12:24 ` Dmitry Baryshkov
2025-01-06 14:49 ` Maxime Ripard
2025-01-07 10:35 ` Dmitry Baryshkov
2025-01-07 15:12 ` Maxime Ripard
2025-01-08 15:24 ` Luca Ceresoli
2025-01-08 15:24 ` Luca Ceresoli
2025-01-08 16:02 ` Maxime Ripard
2025-01-22 16:12 ` Luca Ceresoli
2025-01-28 14:49 ` Maxime Ripard
2025-01-29 11:51 ` Luca Ceresoli
2025-01-29 12:22 ` Dmitry Baryshkov
2025-01-29 13:11 ` Luca Ceresoli
2024-12-31 10:39 ` [PATCH v5 05/10] drm/tests: bridge: add KUnit tests for DRM bridges (init and destroy) Luca Ceresoli
2024-12-31 10:40 ` [PATCH v5 06/10] drm/bridge: ti-sn65dsi83: use dynamic lifetime management Luca Ceresoli
2024-12-31 10:40 ` [PATCH v5 07/10] drm/bridge: panel: " Luca Ceresoli
2024-12-31 10:40 ` [PATCH v5 08/10] drm/bridge: samsung-dsim: use supporting variable for out_bridge Luca Ceresoli
2024-12-31 14:57 ` Dmitry Baryshkov
2025-01-02 12:01 ` Luca Ceresoli
2025-01-03 6:00 ` Dmitry Baryshkov
2025-01-10 10:58 ` Luca Ceresoli
2025-01-16 10:32 ` Luca Ceresoli
2025-01-16 10:56 ` Dmitry Baryshkov
2025-01-21 11:27 ` Luca Ceresoli
2025-01-21 11:57 ` Dmitry Baryshkov
2025-01-28 15:52 ` Maxime Ripard
2025-01-16 12:26 ` Maxime Ripard
2025-01-21 11:27 ` Luca Ceresoli
2025-01-28 15:09 ` Maxime Ripard
2025-01-29 11:51 ` Luca Ceresoli
2025-02-04 15:44 ` Maxime Ripard
2024-12-31 10:40 ` [PATCH v5 09/10] drm/bridge: samsung-dsim: refcount the out_bridge Luca Ceresoli
2024-12-31 14:58 ` Dmitry Baryshkov
2024-12-31 10:40 ` [PATCH v5 10/10] drm/bridge: hotplug-bridge: add driver to support hot-pluggable DSI bridges Luca Ceresoli
2024-12-31 15:29 ` Dmitry Baryshkov
2025-01-02 12:01 ` Luca Ceresoli
2025-09-09 15:29 ` Luca Ceresoli
2025-09-09 15:39 ` Luca Ceresoli
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87seq4nm3g.fsf@intel.com \
--to=jani.nikula@linux.intel.com \
--cc=Laurent.pinchart@ideasonboard.com \
--cc=airlied@gmail.com \
--cc=andrzej.hajda@intel.com \
--cc=catalin.marinas@arm.com \
--cc=contact@paulk.fr \
--cc=corbet@lwn.net \
--cc=danielt@kernel.org \
--cc=dmitry.baryshkov@linaro.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=festevam@gmail.com \
--cc=herve.codina@bootlin.com \
--cc=inki.dae@samsung.com \
--cc=jagan@amarulasolutions.com \
--cc=jernej.skrabec@gmail.com \
--cc=jonas@kwiboo.se \
--cc=kernel@pengutronix.de \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=luca.ceresoli@bootlin.com \
--cc=m.szyprowski@samsung.com \
--cc=maarten.lankhorst@linux.intel.com \
--cc=mripard@kernel.org \
--cc=neil.armstrong@linaro.org \
--cc=paul.kocialkowski@bootlin.com \
--cc=rfoss@kernel.org \
--cc=s.hauer@pengutronix.de \
--cc=shawnguo@kernel.org \
--cc=simona@ffwll.ch \
--cc=thomas.petazzoni@bootlin.com \
--cc=tzimmermann@suse.de \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.