Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

drm/bridge: add list of removed refcounted bridges

Between drm_bridge_add() and drm_bridge_remove() bridges are registered to
the DRM core via the global bridge_list and visible in
/sys/kernel/debug/dri/bridges. However between drm_bridge_remove() and the
last drm_bridge_put() memory is still allocated even though the bridge is
not registered, i.e. not in bridges_list, and also not visible in
debugfs. This prevents debugging refcounted bridges lifetime, especially
leaks due to a missing drm_bridge_put().

In order to allow debugfs to also show the removed bridges, move such
bridges into a new ad-hoc list until they are eventually freed.

Note this requires adding INIT_LIST_HEAD(&bridge->list) in the bridge
initialization code. The lack of such init was not exposing any bug so far,
but it would with the new code, for example when a bridge is allocated and
then freed without calling drm_bridge_add(), which is common on probe
errors.

drm_bridge_add() needs special care for bridges being added after having
been previously added and then removed. This happens for example for many
non-DCS DSI host bridge drivers like samsung-dsim which
drm_bridge_add/remove() themselves every time the DSI device does a DSI
attaches/detach. When the DSI device is hot-pluggable this happens multiple
times in the lifetime of the DSI host bridge. On every attach after the
first one, drm_bridge_add() finds bridge->list in the removed list, not at
the initialized state as drm_bridge_add() currently expects. Add a
list_del_init() to remove the bridge from the lingering list and bring
bridge->list back to the initialized state.

Reviewed-by: Maxime Ripard <mripard@kernel.org>
Link: https://lore.kernel.org/r/20250915-drm-bridge-debugfs-removed-v9-1-6e5c0aff5de9@bootlin.com
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>

+17 -1
+17 -1
drivers/gpu/drm/drm_bridge.c
··· 197 197 * driver. 198 198 */ 199 199 200 + /* Protect bridge_list and bridge_lingering_list */ 200 201 static DEFINE_MUTEX(bridge_lock); 201 202 static LIST_HEAD(bridge_list); 203 + static LIST_HEAD(bridge_lingering_list); 202 204 203 205 static void __drm_bridge_free(struct kref *kref) 204 206 { 205 207 struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount); 206 208 209 + mutex_lock(&bridge_lock); 210 + list_del(&bridge->list); 211 + mutex_unlock(&bridge_lock); 212 + 207 213 if (bridge->funcs->destroy) 208 214 bridge->funcs->destroy(bridge); 215 + 209 216 kfree(bridge->container); 210 217 } 211 218 ··· 280 273 return ERR_PTR(-ENOMEM); 281 274 282 275 bridge = container + offset; 276 + INIT_LIST_HEAD(&bridge->list); 283 277 bridge->container = container; 284 278 bridge->funcs = funcs; 285 279 kref_init(&bridge->refcount); ··· 307 299 DRM_WARN("DRM bridge corrupted or not allocated by devm_drm_bridge_alloc()\n"); 308 300 309 301 drm_bridge_get(bridge); 302 + 303 + /* 304 + * If the bridge was previously added and then removed, it is now 305 + * in bridge_lingering_list. Remove it or bridge_lingering_list will be 306 + * corrupted when adding this bridge to bridge_list below. 307 + */ 308 + if (!list_empty(&bridge->list)) 309 + list_del_init(&bridge->list); 310 310 311 311 mutex_init(&bridge->hpd_mutex); 312 312 ··· 359 343 void drm_bridge_remove(struct drm_bridge *bridge) 360 344 { 361 345 mutex_lock(&bridge_lock); 362 - list_del_init(&bridge->list); 346 + list_move_tail(&bridge->list, &bridge_lingering_list); 363 347 mutex_unlock(&bridge_lock); 364 348 365 349 mutex_destroy(&bridge->hpd_mutex);