drm_bridge_connector_init() adds a drmm-allocated connector. For bridge hotplug drmm allocations are not suitable because a connector may have to be added and removed multiple times in the lifetime of a card.
In preparation for bridge hotplug, add APIs to add and remove a connector using regular non-managed allocations. For the dynamic connector, this requires the kfree() the allocated struct drm_bridge_connector in the destroy func. However that func will be called even when using the pre-existing drmm API, leading to a double free (kfree() in the destroy callback + drmm). One option to avoid this issue is introducing two mostly identical drm_connector_funcs instances, one with .destroy and one without. But that would be an annoying code duplication. Instead take a different approach: always allocate using non-drmm kzalloc_obj(), so that deallocation always happen in destroy->kfree(). Signed-off-by: Luca Ceresoli <[email protected]> --- Uhm, maybe the change from drmm_kzalloc to kzalloc_obj and addition of kfree to the destroy callback should eb a separate commit? --- drivers/gpu/drm/display/drm_bridge_connector.c | 24 +++++++++++++++++++++++- include/drm/drm_bridge_connector.h | 4 ++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index 1c222e27767d..2a0065208fb8 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -285,6 +285,7 @@ static void drm_bridge_connector_dynconn_destroy(struct drm_connector *connector drm_connector_cleanup(connector); drm_bridge_connector_put_bridges(bridge_connector); + kfree(bridge_connector); } static const struct drm_connector_funcs drm_bridge_connector_funcs = { @@ -1184,7 +1185,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, struct drm_connector *connector; int ret; - bridge_connector = drmm_kzalloc(drm, sizeof(*bridge_connector), GFP_KERNEL); + bridge_connector = kzalloc_obj(*bridge_connector); if (!bridge_connector) return ERR_PTR(-ENOMEM); @@ -1199,3 +1200,24 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, return connector; } EXPORT_SYMBOL_GPL(drm_bridge_connector_init); + +struct drm_connector *drm_bridge_connector_dynamic_add(struct drm_device *drm, + struct drm_encoder *encoder) +{ + struct drm_bridge_connector *bridge_connector; + + bridge_connector = kzalloc_obj(*bridge_connector); + if (!bridge_connector) + return ERR_PTR(-ENOMEM); + + return drm_bridge_connector_initialize(bridge_connector, drm, encoder); +} +EXPORT_SYMBOL_GPL(drm_bridge_connector_dynamic_add); + +void drm_bridge_connector_dynamic_remove(struct drm_connector *connector) +{ + struct drm_bridge_connector *bridge_connector = to_drm_bridge_connector(connector); + + drm_bridge_connector_fini(bridge_connector); +} +EXPORT_SYMBOL_GPL(drm_bridge_connector_dynamic_remove); diff --git a/include/drm/drm_bridge_connector.h b/include/drm/drm_bridge_connector.h index 69630815fb09..de6ec91dbfab 100644 --- a/include/drm/drm_bridge_connector.h +++ b/include/drm/drm_bridge_connector.h @@ -9,8 +9,12 @@ struct drm_connector; struct drm_device; struct drm_encoder; +struct drm_bridge_connector; struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, struct drm_encoder *encoder); +struct drm_connector *drm_bridge_connector_dynamic_add(struct drm_device *drm, + struct drm_encoder *encoder); +void drm_bridge_connector_dynamic_remove(struct drm_connector *connector); #endif /* __DRM_BRIDGE_CONNECTOR_H__ */ -- 2.55.0
