mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
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>
This commit is contained in:
@@ -168,6 +168,7 @@ ForEachMacros:
|
|||||||
- 'drm_exec_for_each_locked_object'
|
- 'drm_exec_for_each_locked_object'
|
||||||
- 'drm_exec_for_each_locked_object_reverse'
|
- 'drm_exec_for_each_locked_object_reverse'
|
||||||
- 'drm_for_each_bridge_in_chain'
|
- 'drm_for_each_bridge_in_chain'
|
||||||
|
- 'drm_for_each_bridge_in_chain_scoped'
|
||||||
- 'drm_for_each_connector_iter'
|
- 'drm_for_each_connector_iter'
|
||||||
- 'drm_for_each_crtc'
|
- 'drm_for_each_crtc'
|
||||||
- 'drm_for_each_crtc_reverse'
|
- 'drm_for_each_crtc_reverse'
|
||||||
|
|||||||
@@ -1440,10 +1440,51 @@ drm_bridge_chain_get_last_bridge(struct drm_encoder *encoder)
|
|||||||
* iteration
|
* iteration
|
||||||
*
|
*
|
||||||
* Iterate over all bridges present in the bridge chain attached to @encoder.
|
* 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) \
|
#define drm_for_each_bridge_in_chain(encoder, bridge) \
|
||||||
list_for_each_entry(bridge, &(encoder)->bridge_chain, chain_node)
|
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
|
enum drm_mode_status
|
||||||
drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
|
drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
|
||||||
const struct drm_display_info *info,
|
const struct drm_display_info *info,
|
||||||
|
|||||||
Reference in New Issue
Block a user