From nobody Mon Feb 9 17:22:12 2026 Received: from smtpout-04.galae.net (smtpout-04.galae.net [185.171.202.116]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AEEEC3502A6 for ; Tue, 13 Jan 2026 18:47:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.171.202.116 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768330059; cv=none; b=ugLYVywSSK7Tmmj5AVyfWdODOrwfmGC3swklsRt6Rvxq82beUibHEFsCFiyD/xyZeEPgWzaXyLs/iKYmLLA++ExiY8/W6MIYzyk2VIwBVLGJLSjfhYtPWiSy786pcJF0BQB1FTE+7axWA4nPDlDC1gsT0Eoh3He7lk8iS9swvg4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768330059; c=relaxed/simple; bh=2Tq3pu8Dwz71Lw2/EhHOwgiTiuuCRN166Jcv5ho44Ts=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OZS8n19K1ve5zV/pu+ABR23whO2WgwgosPE3FUAanPMoWG6HUiehw7Cu1V20MiniU4uLaPrSi/hnCIha36nJF6jOY+nzp0peBwiZAgnG7U6jhAwjyuvmKTZnApogyFX8IpfnODPFWCfjJ/EKo0uCbA1KkCIeqKZcQfNWmeEo6b0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=DJ5CPyEL; arc=none smtp.client-ip=185.171.202.116 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="DJ5CPyEL" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id 7A82EC1ECBA; Tue, 13 Jan 2026 18:47:08 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 553936075F; Tue, 13 Jan 2026 18:47:33 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 7F57B103C8707; Tue, 13 Jan 2026 19:47:30 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1768330052; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=7WXp3t4en/N2gz9isbtng0HaQOEi821ijFkC1dJtqcU=; b=DJ5CPyEL4Mz72zlKR+8bHlU0UapbFLkbYAYVOFmYVcoam1pZMGozm6WFWTG9IJZtbvDNMy ia60mf5yPZlnc9jSEZxz/m5bXbR0CwEEqgpGRaFbbh9eRU8YWFRNVoJcpGvZBK/Ig7fgBp 1JTDmpoYCybdJzAlUQuFn5UjwY09+WuDByWdCuTI6k8Lq1LIl6sxbD6cyDvApOJaZkX3gJ JCa2Ooa9rp4K/tVBWzjeDXhUEjld36IeCvNzXrFLoDr20T4B8rMO1bEQd9Ab6wQt23GmNh hJRKRihMlUQDe7uu2vmEncojpoc6NrxRqVWoJ3WTiBuz0fb/ExRjChxcUl1OHQ== From: Luca Ceresoli Date: Tue, 13 Jan 2026 19:47:15 +0100 Subject: [PATCH RESEND v4 4/7] drm/bridge: lock the encoder chain in scoped for_each loops Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260113-drm-bridge-alloc-encoder-chain-mutex-v4-4-60f3135adc45@bootlin.com> References: <20260113-drm-bridge-alloc-encoder-chain-mutex-v4-0-60f3135adc45@bootlin.com> In-Reply-To: <20260113-drm-bridge-alloc-encoder-chain-mutex-v4-0-60f3135adc45@bootlin.com> To: Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Andrzej Hajda , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec Cc: Hui Pu , Thomas Petazzoni , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Luca Ceresoli , Ian Ray X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 drm_for_each_bridge_in_chain_scoped() and drm_for_each_bridge_in_chain_from() currently get/put the bridge at each iteration. But they don't protect the encoder chain, so it could change (bridges added/removed) while some code is iterating over the list itself. Such code can then derail on incorrect pointers. To make iterations safe, augment these for_each macros to lock the encoder chain mutex at the beginning and unlock it at the end of the loop (be it at the end of the list, or earlier due to a 'break' or 'return' statement). This change requires more operations when starting and ending the loop. To avoid making the macros even more complex, move these operations to helper functions. Also remname some of the existing helper functions for consistency. Signed-off-by: Luca Ceresoli --- Changed in v4: - Slightly improve kerneldoc to clarify a bridge reference is held in addition to the mutex Changed in v3: - Re-add drm_bridge_get/put() Changed in v2: - Fixed infinite loop in drm_for_each_bridge_in_chain_scoped() when encoder->bridge_chain is empty, reported here: https://lore.kernel.org/lkml/202509301358.38036b85-lkp@intel.com/ - Slightly improved commit message --- include/drm/drm_bridge.h | 73 +++++++++++++++++++++++++++++++-------------= ---- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 0ff7ab4aa868..3d26dde1ac81 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -1440,26 +1440,37 @@ drm_bridge_chain_get_last_bridge(struct drm_encoder= *encoder) struct drm_bridge, chain_node)); } =20 -/** - * 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 l= ast. - */ -static inline struct drm_bridge * -drm_bridge_get_next_bridge_and_put(struct drm_bridge *bridge) +/* Internal to drm_for_each_bridge_in_chain*() */ +static inline struct drm_bridge *__drm_for_each_bridge_in_chain_next(struc= t drm_bridge *bridge) { struct drm_bridge *next =3D drm_bridge_get_next_bridge(bridge); =20 + if (!next) + mutex_unlock(&bridge->encoder->bridge_chain_mutex); + drm_bridge_put(bridge); =20 return next; } =20 +/* Internal to drm_for_each_bridge_in_chain*() */ +DEFINE_FREE(__drm_for_each_bridge_in_chain_cleanup, struct drm_bridge *, + if (_T) { mutex_unlock(&_T->encoder->bridge_chain_mutex); drm_bridge_put(= _T); }) + +/* Internal to drm_for_each_bridge_in_chain_scoped() */ +static inline struct drm_bridge * +__drm_for_each_bridge_in_chain_scoped_start(struct drm_encoder *encoder) +{ + mutex_lock(&encoder->bridge_chain_mutex); + + struct drm_bridge *bridge =3D drm_bridge_chain_get_first_bridge(encoder); + + if (!bridge) + mutex_unlock(&encoder->bridge_chain_mutex); + + return bridge; +} + /** * drm_for_each_bridge_in_chain_scoped - iterate over all bridges attached * to an encoder @@ -1469,14 +1480,24 @@ drm_bridge_get_next_bridge_and_put(struct drm_bridg= e *bridge) * * Iterate over all bridges present in the bridge chain attached to @encod= er. * - * Automatically gets/puts the bridge reference while iterating, and puts - * the reference even if returning or breaking in the middle of the loop. + * Automatically gets/puts the bridge reference while iterating and locks + * the encoder chain mutex to prevent chain modifications while iterating. */ -#define drm_for_each_bridge_in_chain_scoped(encoder, bridge) \ - for (struct drm_bridge *bridge __free(drm_bridge_put) =3D \ - drm_bridge_chain_get_first_bridge(encoder); \ - bridge; \ - bridge =3D drm_bridge_get_next_bridge_and_put(bridge)) +#define drm_for_each_bridge_in_chain_scoped(encoder, bridge) \ + for (struct drm_bridge *bridge __free(__drm_for_each_bridge_in_chain_clea= nup) =3D \ + __drm_for_each_bridge_in_chain_scoped_start((encoder)); \ + bridge; \ + bridge =3D __drm_for_each_bridge_in_chain_next(bridge)) \ + +/* Internal to drm_for_each_bridge_in_chain_from() */ +static inline struct drm_bridge * +__drm_for_each_bridge_in_chain_from_start(struct drm_bridge *bridge) +{ + drm_bridge_get(bridge); + mutex_lock(&bridge->encoder->bridge_chain_mutex); + + return bridge; +} =20 /** * drm_for_each_bridge_in_chain_from - iterate over all bridges starting @@ -1488,14 +1509,14 @@ drm_bridge_get_next_bridge_and_put(struct drm_bridg= e *bridge) * Iterate over all bridges in the encoder chain starting from * @first_bridge, included. * - * Automatically gets/puts the bridge reference while iterating, and puts - * the reference even if returning or breaking in the middle of the loop. + * Automatically gets/puts the bridge reference while iterating and locks + * the encoder chain mutex to prevent chain modifications while iterating. */ -#define drm_for_each_bridge_in_chain_from(first_bridge, bridge) \ - for (struct drm_bridge *bridge __free(drm_bridge_put) =3D \ - drm_bridge_get(first_bridge); \ - bridge; \ - bridge =3D drm_bridge_get_next_bridge_and_put(bridge)) +#define drm_for_each_bridge_in_chain_from(first_bridge, bridge) \ + for (struct drm_bridge *bridge __free(__drm_for_each_bridge_in_chain_clea= nup) =3D \ + __drm_for_each_bridge_in_chain_from_start(first_bridge); \ + bridge; \ + bridge =3D __drm_for_each_bridge_in_chain_next(bridge)) \ =20 enum drm_mode_status drm_bridge_chain_mode_valid(struct drm_bridge *bridge, --=20 2.52.0