Commit e46efc6a authored by Luca Ceresoli's avatar Luca Ceresoli
Browse files

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: default avatarMaxime 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: default avatarLuca Ceresoli <luca.ceresoli@bootlin.com>
parent c92f59ba
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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'
+41 −0
Original line number Diff line number Diff line
@@ -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,