diff mbox series

[v2,34/34] drm/bridge: panel: convert to devm_drm_bridge_alloc() API

Message ID 20250424-drm-bridge-convert-to-alloc-api-v2-34-8f91a404d86b@bootlin.com
State Superseded
Headers show
Series drm: convert all bridges to devm_drm_bridge_alloc() | expand

Commit Message

Luca Ceresoli April 24, 2025, 8:05 p.m. UTC
This is the new API for allocating DRM bridges.

The devm lifetime management of this driver is peculiar. The underlying
device for the panel_bridge is the panel, and the devm lifetime is tied the
panel device (panel->dev). However the panel_bridge allocation is not
performed by the panel driver, but rather by a separate entity (typically
the previous bridge in the encoder chain).

Thus when that separate entoty is destroyed, the panel_bridge is not
removed automatically by devm, so it is rather done explicitly by calling
drm_panel_bridge_remove(). This is the function that does devm_kfree() the
panel_bridge in current code, so update it as well to put the bridge
reference instead.

Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
 drivers/gpu/drm/bridge/panel.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

Comments

Maxime Ripard April 28, 2025, 11:39 a.m. UTC | #1
On Thu, Apr 24, 2025 at 10:05:49PM +0200, Luca Ceresoli wrote:
> This is the new API for allocating DRM bridges.
> 
> The devm lifetime management of this driver is peculiar. The underlying
> device for the panel_bridge is the panel, and the devm lifetime is tied the
> panel device (panel->dev). However the panel_bridge allocation is not
> performed by the panel driver, but rather by a separate entity (typically
> the previous bridge in the encoder chain).
> 
> Thus when that separate entoty is destroyed, the panel_bridge is not
> removed automatically by devm, so it is rather done explicitly by calling
> drm_panel_bridge_remove(). This is the function that does devm_kfree() the
> panel_bridge in current code, so update it as well to put the bridge
> reference instead.
> 
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>

This looks fine, but we need a TODO entry to clean this up later on, and
a comment on devm_drm_put_bridge that this is inherently unsafe and
must not be used.

Maxime
Luca Ceresoli April 28, 2025, 3:25 p.m. UTC | #2
Hi Maxime,

On Mon, 28 Apr 2025 13:39:23 +0200
Maxime Ripard <mripard@kernel.org> wrote:

> On Thu, Apr 24, 2025 at 10:05:49PM +0200, Luca Ceresoli wrote:
> > This is the new API for allocating DRM bridges.
> > 
> > The devm lifetime management of this driver is peculiar. The underlying
> > device for the panel_bridge is the panel, and the devm lifetime is tied the
> > panel device (panel->dev). However the panel_bridge allocation is not
> > performed by the panel driver, but rather by a separate entity (typically
> > the previous bridge in the encoder chain).
> > 
> > Thus when that separate entoty is destroyed, the panel_bridge is not
> > removed automatically by devm, so it is rather done explicitly by calling
> > drm_panel_bridge_remove(). This is the function that does devm_kfree() the
> > panel_bridge in current code, so update it as well to put the bridge
> > reference instead.
> > 
> > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>  
> 
> This looks fine, but we need a TODO entry to clean this up later on, and
> a comment on devm_drm_put_bridge that this is inherently unsafe and
> must not be used.

Ah, I see, OK.

Quick draft:

 /**
  * devm_drm_put_bridge - Release a bridge reference obtained via devm
  * @dev: device that got the bridge via devm
  * @bridge: pointer to a struct drm_bridge obtained via devm
  *
  * Same as drm_bridge_put() for bridge pointers obtained via devm functions
  * such as devm_drm_bridge_alloc().
+ *
+ * This function is a temporary workaround and MUST NOT be used. Manual
+ * handling of bridge lifetime is inherently unsafe.
  */

and:

-	devm_kfree(panel_bridge->panel->dev, bridge);
+       /* TODO remove this after reworking panel_bridge lifetime */
+	devm_drm_put_bridge(panel_bridge->panel->dev, bridge);
 }

Does it look good enough?

Luca
Maxime Ripard May 5, 2025, 6:23 a.m. UTC | #3
On Mon, Apr 28, 2025 at 05:25:16PM +0200, Luca Ceresoli wrote:
> Hi Maxime,
> 
> On Mon, 28 Apr 2025 13:39:23 +0200
> Maxime Ripard <mripard@kernel.org> wrote:
> 
> > On Thu, Apr 24, 2025 at 10:05:49PM +0200, Luca Ceresoli wrote:
> > > This is the new API for allocating DRM bridges.
> > > 
> > > The devm lifetime management of this driver is peculiar. The underlying
> > > device for the panel_bridge is the panel, and the devm lifetime is tied the
> > > panel device (panel->dev). However the panel_bridge allocation is not
> > > performed by the panel driver, but rather by a separate entity (typically
> > > the previous bridge in the encoder chain).
> > > 
> > > Thus when that separate entoty is destroyed, the panel_bridge is not
> > > removed automatically by devm, so it is rather done explicitly by calling
> > > drm_panel_bridge_remove(). This is the function that does devm_kfree() the
> > > panel_bridge in current code, so update it as well to put the bridge
> > > reference instead.
> > > 
> > > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>  
> > 
> > This looks fine, but we need a TODO entry to clean this up later on, and
> > a comment on devm_drm_put_bridge that this is inherently unsafe and
> > must not be used.
> 
> Ah, I see, OK.
> 
> Quick draft:
> 
>  /**
>   * devm_drm_put_bridge - Release a bridge reference obtained via devm
>   * @dev: device that got the bridge via devm
>   * @bridge: pointer to a struct drm_bridge obtained via devm
>   *
>   * Same as drm_bridge_put() for bridge pointers obtained via devm functions
>   * such as devm_drm_bridge_alloc().
> + *
> + * This function is a temporary workaround and MUST NOT be used. Manual
> + * handling of bridge lifetime is inherently unsafe.
>   */

That part looks good to me

> and:
> 
> -	devm_kfree(panel_bridge->panel->dev, bridge);
> +       /* TODO remove this after reworking panel_bridge lifetime */
> +	devm_drm_put_bridge(panel_bridge->panel->dev, bridge);
>  }
> 
> Does it look good enough?

That too, but I was talking about an entry in
https://www.kernel.org/doc/html/latest/gpu/todo.html

Maxime
Luca Ceresoli May 5, 2025, 3:20 p.m. UTC | #4
On Mon, 5 May 2025 08:23:26 +0200
Maxime Ripard <mripard@kernel.org> wrote:

> On Mon, Apr 28, 2025 at 05:25:16PM +0200, Luca Ceresoli wrote:
> > Hi Maxime,
> > 
> > On Mon, 28 Apr 2025 13:39:23 +0200
> > Maxime Ripard <mripard@kernel.org> wrote:
> >   
> > > On Thu, Apr 24, 2025 at 10:05:49PM +0200, Luca Ceresoli wrote:  
> > > > This is the new API for allocating DRM bridges.
> > > > 
> > > > The devm lifetime management of this driver is peculiar. The underlying
> > > > device for the panel_bridge is the panel, and the devm lifetime is tied the
> > > > panel device (panel->dev). However the panel_bridge allocation is not
> > > > performed by the panel driver, but rather by a separate entity (typically
> > > > the previous bridge in the encoder chain).
> > > > 
> > > > Thus when that separate entoty is destroyed, the panel_bridge is not
> > > > removed automatically by devm, so it is rather done explicitly by calling
> > > > drm_panel_bridge_remove(). This is the function that does devm_kfree() the
> > > > panel_bridge in current code, so update it as well to put the bridge
> > > > reference instead.
> > > > 
> > > > Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>    
> > > 
> > > This looks fine, but we need a TODO entry to clean this up later on, and
> > > a comment on devm_drm_put_bridge that this is inherently unsafe and
> > > must not be used.  
> > 
> > Ah, I see, OK.
> > 
> > Quick draft:
> > 
> >  /**
> >   * devm_drm_put_bridge - Release a bridge reference obtained via devm
> >   * @dev: device that got the bridge via devm
> >   * @bridge: pointer to a struct drm_bridge obtained via devm
> >   *
> >   * Same as drm_bridge_put() for bridge pointers obtained via devm functions
> >   * such as devm_drm_bridge_alloc().
> > + *
> > + * This function is a temporary workaround and MUST NOT be used. Manual
> > + * handling of bridge lifetime is inherently unsafe.
> >   */  
> 
> That part looks good to me
> 
> > and:
> > 
> > -	devm_kfree(panel_bridge->panel->dev, bridge);
> > +       /* TODO remove this after reworking panel_bridge lifetime */
> > +	devm_drm_put_bridge(panel_bridge->panel->dev, bridge);
> >  }
> > 
> > Does it look good enough?  
> 
> That too, but I was talking about an entry in
> https://www.kernel.org/doc/html/latest/gpu/todo.html

Ah, sure!

I queued this for v3, if you have better suggestions don't hesitate to
let me know:

-----8<-----

Remove devm_drm_put_bridge()
----------------------------

Due to how the panel bridge handles the drm_bridge object lifetime, special
care must be taken to dispose of the drm_bridge object when the
panel_bridge is removed. This is currently managed using
devm_drm_put_bridge(), but that is an unsafe, temporary workaround. To fix
that, the DRM panel lifetime needs to be reworked. After the rework is
done, remove devm_drm_put_bridge() and the TODO in drm_panel_bridge_remove().

Contact: Maxime Ripard <mripard@kernel.org>,
         Luca Ceresoli <luca.ceresoli@bootlin.com>

Level: Intermediate

-----8<-----

Luca
diff mbox series

Patch

diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 79b009ab9396048eac57ad47631a902e949d77c6..ddd1e91970d09b93aa64f50cd9155939a12a2c6f 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -287,15 +287,14 @@  struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
 	if (!panel)
 		return ERR_PTR(-EINVAL);
 
-	panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
-				    GFP_KERNEL);
-	if (!panel_bridge)
-		return ERR_PTR(-ENOMEM);
+	panel_bridge = devm_drm_bridge_alloc(panel->dev, struct panel_bridge, bridge,
+					     &panel_bridge_bridge_funcs);
+	if (IS_ERR(panel_bridge))
+		return (void *)panel_bridge;
 
 	panel_bridge->connector_type = connector_type;
 	panel_bridge->panel = panel;
 
-	panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs;
 	panel_bridge->bridge.of_node = panel->dev->of_node;
 	panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES;
 	panel_bridge->bridge.type = connector_type;
@@ -327,7 +326,7 @@  void drm_panel_bridge_remove(struct drm_bridge *bridge)
 	panel_bridge = drm_bridge_to_panel_bridge(bridge);
 
 	drm_bridge_remove(bridge);
-	devm_kfree(panel_bridge->panel->dev, bridge);
+	devm_drm_put_bridge(panel_bridge->panel->dev, bridge);
 }
 EXPORT_SYMBOL(drm_panel_bridge_remove);