diff options
| author | Luca Ceresoli <luca.ceresoli@bootlin.com> | 2025-08-08 16:49:10 +0200 |
|---|---|---|
| committer | Luca Ceresoli <luca.ceresoli@bootlin.com> | 2025-09-16 15:02:13 +0200 |
| commit | e46efc6a7d288830c4aeaf3c65c7e913a8ca35d7 (patch) | |
| tree | 0cdc5351bb57f0ee5f44882fa531721ddb411a4e | |
| parent | c92f59bac078465425f1223b29a8d63c89027eba (diff) | |
drm/bridge: add drm_for_each_bridge_in_chain_scoped()
drm_for_each_bridge_in_chain() iterates ofer the bridges in an encoder
chain without protecting the lifetime of the bridges using
drm_bridge_get/put(). This creates a risk window where the bridge could be
freed while iterating on it. Users of drm_for_each_bridge_in_chain() cannot
solve this reliably.
Add variant of drm_for_each_bridge_in_chain() that gets/puts the bridge
reference at the beginning/end of each iteration, and puts it if breaking
ot of the loop.
Note that this requires adding a new drm_bridge_get_next_bridge_and_put()
function because, unlike similar functions as __of_get_next_child(),
drm_bridge_get_next_bridge() gets the "next" pointer but does not put the
"prev" pointer. Unfortunately drm_bridge_get_next_bridge() cannot be
modified to put the "prev" pointer because some of its users rely on
this, such as drm_atomic_bridge_propagate_bus_flags().
Also deprecate drm_for_each_bridge_in_chain(), in preparation for removing
it after converting all users to the scoped version.
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Link: https://lore.kernel.org/r/20250808-drm-bridge-alloc-getput-for_each_bridge-v2-3-edb6ee81edf1@bootlin.com
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
| -rw-r--r-- | .clang-format | 1 | ||||
| -rw-r--r-- | include/drm/drm_bridge.h | 41 |
2 files changed, 42 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format index 48405c54ef27..1cac7d497664 100644 --- a/.clang-format +++ b/.clang-format @@ -168,6 +168,7 @@ ForEachMacros: - 'drm_exec_for_each_locked_object' - 'drm_exec_for_each_locked_object_reverse' - 'drm_for_each_bridge_in_chain' + - 'drm_for_each_bridge_in_chain_scoped' - 'drm_for_each_connector_iter' - 'drm_for_each_crtc' - 'drm_for_each_crtc_reverse' diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 76e05930f50e..57a3d3cd08e7 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -1440,10 +1440,51 @@ drm_bridge_chain_get_last_bridge(struct drm_encoder *encoder) * iteration * * Iterate over all bridges present in the bridge chain attached to @encoder. + * + * This is deprecated, do not use! + * New drivers shall use drm_for_each_bridge_in_chain_scoped(). */ #define drm_for_each_bridge_in_chain(encoder, bridge) \ list_for_each_entry(bridge, &(encoder)->bridge_chain, chain_node) +/** + * drm_bridge_get_next_bridge_and_put - Get the next bridge in the chain + * and put the previous + * @bridge: bridge object + * + * Same as drm_bridge_get_next_bridge() but additionally puts the @bridge. + * + * RETURNS: + * the next bridge in the chain after @bridge, or NULL if @bridge is the last. + */ +static inline struct drm_bridge * +drm_bridge_get_next_bridge_and_put(struct drm_bridge *bridge) +{ + struct drm_bridge *next = drm_bridge_get_next_bridge(bridge); + + drm_bridge_put(bridge); + + return next; +} + +/** + * drm_for_each_bridge_in_chain_scoped - iterate over all bridges attached + * to an encoder + * @encoder: the encoder to iterate bridges on + * @bridge: a bridge pointer updated to point to the current bridge at each + * iteration + * + * Iterate over all bridges present in the bridge chain attached to @encoder. + * + * Automatically gets/puts the bridge reference while iterating, and puts + * the reference even if returning or breaking in the middle of the loop. + */ +#define drm_for_each_bridge_in_chain_scoped(encoder, bridge) \ + for (struct drm_bridge *bridge __free(drm_bridge_put) = \ + drm_bridge_chain_get_first_bridge(encoder); \ + bridge; \ + bridge = drm_bridge_get_next_bridge_and_put(bridge)) + enum drm_mode_status drm_bridge_chain_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, |
