From nobody Thu Apr 2 22:05:40 2026 Received: from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56]) (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 D94EF3D8126 for ; Tue, 24 Mar 2026 08:58:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.84.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774342709; cv=none; b=Y9m5ORLOx25RY1+xFvR/HHYuMj9uNGbA7qjbWWvYRZUNUncWMSSWnooNGOpVH/PP8XGvemrMVsUM8jGPadV9n8HMAt3lHJ0uEQF55nRnWTYjW1c9kyTScMELY4WdJAuKUs5RlEwxwQo3Xfi9V0N/OxMB/0Zfj7SDJZ7J6DAcY0A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774342709; c=relaxed/simple; bh=ErbyMvuDMLC/MO4CBEUXTvzkh4RFhoVFzYfTTQaIexk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=kuRwJSawqDwkHPlEYJSNDXHnBQ/MECdaU07ixMr900gFiUGREZ8KiiXumSw9I1CoiXMieWZpe/J7B+1XjIMngnJ//k9zSgfxTd0wzCv9VQdjt9XHL0EM9ViQLiV8SXnUAb9dRhgGeEsLt6ear9QgQMMxZreHlgK4TRmdKKzydcU= 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=newzPJl9; arc=none smtp.client-ip=185.246.84.56 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="newzPJl9" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 500A11A2FC1; Tue, 24 Mar 2026 08:58:26 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 1E8E16011D; Tue, 24 Mar 2026 08:58:26 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id C31F110450F4C; Tue, 24 Mar 2026 09:58:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1774342705; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=TUGbOsIY0mCBqRd2VsrTNDFjTrEiiFOg8V/6WFL6PJI=; b=newzPJl9C2yQ3YixFSm+vHc5j3V+1/xcAh99YXaTn3Mrz++ioL3+VNihAU4wkMKoq/i5gW K/YBglsL0XzcPbMvRUtAE1uFfXg5nl2uN/zceFJDZsjsGfygts0DhuI210KomtmxU0VrxQ An2Z8TY6tkz4YOlC8gVo14pwq/R/Iy146L0a2kQ6TDWPgyh24Nh3uHVlrCm2Nrvq+H3//A sSAM7P1IhBrgGnc+HWQbCg3cs0nzmLPvGCDUpUCFpcWC+jFHWCWVEEXf7ZbIZrkMAKShsH +P303nrR/zFFHMUK9jreVSyLB1uE5x2tOisRvaOWiP5SdXEGCjRcb+cumLA7HQ== From: Luca Ceresoli Date: Tue, 24 Mar 2026 09:58:08 +0100 Subject: [PATCH v5 1/7] drm/encoder: add mutex to protect the bridge chain 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: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-1-8bf786c5c7e6@bootlin.com> References: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@bootlin.com> In-Reply-To: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@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, Ian Ray , Luca Ceresoli X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 The per-encoder bridge chain is currently assumed to be static once it is fully initialized. Work is in progress to add hot-pluggable bridges, breaking that assumption. With bridge removal, the encoder chain can change without notice, removing tail bridges. This can be problematic while iterating over the chain. Add a mutex to be taken whenever looping or changing the encoder chain. Reviewed-by: Maxime Ripard Signed-off-by: Luca Ceresoli --- Changes in v3: - Removed the drm_encoder_chain_[un]lock() wrappers Changes in v2: - Added documentation to new APIs --- drivers/gpu/drm/drm_encoder.c | 2 ++ include/drm/drm_encoder.h | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 8f2bc6a28482..3261f142baea 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -129,6 +129,7 @@ static int __drm_encoder_init(struct drm_device *dev, } =20 INIT_LIST_HEAD(&encoder->bridge_chain); + mutex_init(&encoder->bridge_chain_mutex); list_add_tail(&encoder->head, &dev->mode_config.encoder_list); encoder->index =3D dev->mode_config.num_encoder++; =20 @@ -202,6 +203,7 @@ void drm_encoder_cleanup(struct drm_encoder *encoder) kfree(encoder->name); list_del(&encoder->head); dev->mode_config.num_encoder--; + mutex_destroy(&encoder->bridge_chain_mutex); =20 memset(encoder, 0, sizeof(*encoder)); } diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index 977a9381c8ba..eded7c34481a 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -25,6 +25,7 @@ =20 #include #include +#include #include #include #include @@ -189,6 +190,9 @@ struct drm_encoder { */ struct list_head bridge_chain; =20 + /** @bridge_chain_mutex: protect bridge_chain from changes while iteratin= g */ + struct mutex bridge_chain_mutex; + const struct drm_encoder_funcs *funcs; const struct drm_encoder_helper_funcs *helper_private; =20 --=20 2.53.0 From nobody Thu Apr 2 22:05:40 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 177A23D88EF for ; Tue, 24 Mar 2026 08:58:30 +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=1774342712; cv=none; b=DC8tqwixyyasHIEgqgGdjs5rIA/h+u3+6QfTwy55O8FhxilLIt5metyiULH3tOGgG7grz3/SJGr3VEm3AVAK5MZ5MJMUKmYLDreXZXZNZCHTk4vUaH0d+yZTU0MRJ44RByZdohl0RbPkyamaFUvCzSgn7IK8o+kqr1PyPLSNNLQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774342712; c=relaxed/simple; bh=eNtjihbrkl84/j2G6JECO6KX4FVERIcgBs41yrOadME=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=K447Iq6b7eUua53stTSC8WYxprPKM78A4Gaeqsskf/5GA15ScxokFIm5ZpbyYZmYeBL01TdCn5wXAr316XrKKmVgSmL6kWs4LcB6A8Pnj0qZohYseHbuYZev8yvBrIQDExJDBG1bxVpa1SxmgbdIpYr5GBid6iVJ5HOxP5MqTZE= 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=V2sj8kij; 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="V2sj8kij" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id E5E02C58095; Tue, 24 Mar 2026 08:58:56 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id B4F536011D; Tue, 24 Mar 2026 08:58:29 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id DB98E10450FBD; Tue, 24 Mar 2026 09:58:25 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1774342708; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=dTPjWWPYXj8pYDGBW4Mu8+D8bWZWJwrgTHxQSd0GcUQ=; b=V2sj8kijmrig/BHwN7UJAGn4Bj/smYmcny7KCaHMCSnkX/Og8pP4P3rGPsw1BMcT8ZEba8 Qluj6SoLFGV0OlC7Inuh5spElqszNzX3u1ICx4Ex6hlO4Kkk9EH0DcGEabF4hGJADVT9g0 gJCZbIsNVAVQCEmRCjKFQ9wyWEx+0y2alxyjTwcrznYC+tVb875zWsnckJ2lLZsEXcFwCV hmM6/eSDobo2JnTwPJV8GzCyxe6dllyLbM6SYFSgE5e7plTAb0bzmum1gnzHmofgIa2V6I BWBA0BX+WvBYP9Fl978GB4KK0gLE3Tp5mtidGO6OdTDeX5p76tX6t0lQqH0spA== From: Luca Ceresoli Date: Tue, 24 Mar 2026 09:58:09 +0100 Subject: [PATCH v5 2/7] drm/encoder: drm_encoder_cleanup: lock the encoder chain mutex during removal 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: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-2-8bf786c5c7e6@bootlin.com> References: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@bootlin.com> In-Reply-To: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@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, Ian Ray , Luca Ceresoli X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 drm_encoder_cleanup() modifies the encoder chain by removing bridges via drm_bridge_detach(). Protect this whole operation by taking the mutex, so that: * any users iterating over the chain will not access it during the change * other code willing to modify the list (drm_bridge_attach()) will wait until drm_encoder_cleanup() is done Note that the _safe macro in use here is providing a different and orthogonal kind of protection than the mutex: 1. list_for_each_entry_safe() allows removing the current entry from the list it is iterating on, synchronously; the non-safe version would be unable to find the next entry after the current entry has been removed 2. the mutex being added allows to ensure that the list is not used asynchronously by other code while it is being modified; this prevents such other concurrent code to derail because it is iterating over an element while it is removed The _safe macro, which works by taking the "next" pointer in addition to the "current" one, does not even try to provide the protection at item 2 above. This is visible e.g. when the "next" element is removed by other concurrent code. This is what would happen without the added mutex: 1. start loop: list_for_each_entry_safe(pos, n, ...) sets: pos =3D list_first_entry() =3D (bridge 1) n =3D list_next_entry(pos) =3D (bridge 2) 2. enter the loop 1st time, do something with *pos (bridge 1) 3. in the meanwhile bridge 2 is hot-unplugged -> another thread removes bridge 2 -> drm_bridge_detach() -> list_del() sets (bridge 2)->next =3D LIST_POISON1 4. loop iteration 1 finishes, list_for_each_entry_safe() sets: pos =3D n (previously set to bridge 2) n =3D (bridge 2)->next =3D LIST_POISON1 5. enter the loop 2nd time, do something with *pos (bridge 2) 6. loop iteration 2 finishes, list_for_each_entry_safe() sets: pos =3D n =3D LIST_POISON1 =3D=3D> bug! However, simply adding mutex_[un]lock(&encoder->bridge_chain_mutex) before/after the list_for_each_entry_safe() seems a simple and good solution, but it is introducing a possible ABBA deadlock (found by PROVE_LOCKING). The two code paths involved are: * drm_encoder_cleanup(): - takes the bridge_chain_mutex (A) - calls drm_bridge_detach -> drm_atomic_private_obj_fini -> DRM_MODESET_LOCK_ALL_BEGIN() which takes all locks in the acquisition context (B) * drm_mode_getconnector() (and other code paths): - calls drm_helper_probe_single_connector_modes() which: - takes a drm_modeset_lock in the acquisition context (B) - calls __drm_helper_update_and_validate -> drm_bridge_chain_mode_valid -> drm_for_each_bridge_in_chain_from() which takes the bridge_chain_mutex (A) To avoid this potential ABBA deadlock, move all list items to a temporary list while holding the bridge_chain_mutex, then detach all elements from the temporary list without the mutex. Signed-off-by: Luca Ceresoli --- Changes in v5: - Small commit message improvement Changes in v3: - Prevent ABBA deadlock by using a temporary list - Improve commit message Changes in v2: - Expanded commit messge with rationale, as discussed --- drivers/gpu/drm/drm_encoder.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 3261f142baea..0d5dbed06db4 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -189,14 +189,26 @@ void drm_encoder_cleanup(struct drm_encoder *encoder) { struct drm_device *dev =3D encoder->dev; struct drm_bridge *bridge, *next; + LIST_HEAD(tmplist); =20 /* Note that the encoder_list is considered to be static; should we * remove the drm_encoder at runtime we would have to decrement all * the indices on the drm_encoder after us in the encoder_list. */ =20 - list_for_each_entry_safe(bridge, next, &encoder->bridge_chain, - chain_node) + /* + * We need the bridge_chain_mutex to modify the chain, but + * drm_bridge_detach() will call DRM_MODESET_LOCK_ALL_BEGIN() (in + * drm_modeset_lock_fini()), resulting in a possible ABBA circular + * deadlock. Avoid it by first moving all the bridges to a + * temporary list holding the lock, and then calling + * drm_bridge_detach() without the lock. + */ + mutex_lock(&encoder->bridge_chain_mutex); + list_cut_before(&tmplist, &encoder->bridge_chain, &encoder->bridge_chain); + mutex_unlock(&encoder->bridge_chain_mutex); + + list_for_each_entry_safe(bridge, next, &tmplist, chain_node) drm_bridge_detach(bridge); =20 drm_mode_object_unregister(dev, &encoder->base); --=20 2.53.0 From nobody Thu Apr 2 22:05:40 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 0F5D23D8132 for ; Tue, 24 Mar 2026 08:58:34 +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=1774342716; cv=none; b=ftVx82Lcq9i6k0fwjGRdW643IGCq5aBznpx36JuYGtKl/WE+hZmEfT81go8ukB9Cy6y1HNB8WJOw1Kr781qBbGaxp2u+bxoxJC94sbZOyP/mR5DnPGoyV2hxh03Dv80d1WxuTPl6AYqKAWbT96Yar5hTvXsomS6eYqnbEXjYCGM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774342716; c=relaxed/simple; bh=NXDi1bIBY0bfZZ9JMkXcDgOIGqVyd+NtlhlbI3nMbGg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=EFyESVs9XLa0Yscq7CUAN5RbxNar/qA2hUin7XpqmqloJp2j77SRnGkw+sWQITEBUQSE6zY6WRxvpqbv6V4WXO0sQRCNR7w6YsMfVyotbPaNR6TJt8+ocFyn51yR8LXmAxneFROXM6Rl7qu+pTHFQQJ2QhA9x8BGwsxmGVE3JWQ= 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=k6uW2/ZG; 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="k6uW2/ZG" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id D2B5EC58095; Tue, 24 Mar 2026 08:59:00 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id A23BB6011D; Tue, 24 Mar 2026 08:58:33 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 8536E10450F8E; Tue, 24 Mar 2026 09:58:29 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1774342712; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=2dUn42zD2E9NQa8EnfP56hhRdT/h4FqbY5w3v4OIxuc=; b=k6uW2/ZGLM5rV3ms+nU0LEbdm86usDQAl1wXD7nBjHiSboqEq131NI+ns0czp9D4E7RSZc u9Cqx6WfojEHXGGWCjIr8p5pCr+2OE4pqr7WLyYmtnIa0JwqcKTtUPPCqympm5vst8Z1/l QMcwZzBY8NvoAAX2ZqbxysRMj2APScDdBBCj38nYtcFNuXLidwWR1y52Pe0VB5VKTfaMVO RPZe6Kwom8RZvkBz18Igsi9e3RhqhdVqqg3IMIidJyK6Anr9JkzJ9Yzk+TB3EguGfP4MYa ZbkdosBCzQ7+mspjrRbT6aH4UdvxiVKHh9L+zt7PWM/lUi0B20J2vEoZBaXOHA== From: Luca Ceresoli Date: Tue, 24 Mar 2026 09:58:10 +0100 Subject: [PATCH v5 3/7] drm/bridge: drm_bridge_attach: lock the encoder chain mutex during insertion 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: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-3-8bf786c5c7e6@bootlin.com> References: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@bootlin.com> In-Reply-To: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@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, Ian Ray , Luca Ceresoli X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 drm_bridge_attach() modifies the encoder bridge chain, so take a mutex around such operations to allow users of the chain to protect themselves from chain modifications while iterating. Reviewed-by: Maxime Ripard Signed-off-by: Luca Ceresoli --- Changes in v3: - Lock encoder->bridge_chain_mutex directly, no wrappers Changes in v2: - Removed comment before on drm_bridge_detach() --- drivers/gpu/drm/drm_bridge.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 30d957675d87..42ca4b243579 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -574,10 +574,12 @@ int drm_bridge_attach(struct drm_encoder *encoder, st= ruct drm_bridge *bridge, bridge->dev =3D encoder->dev; bridge->encoder =3D encoder; =20 + mutex_lock(&encoder->bridge_chain_mutex); if (previous) list_add(&bridge->chain_node, &previous->chain_node); else list_add(&bridge->chain_node, &encoder->bridge_chain); + mutex_unlock(&encoder->bridge_chain_mutex); =20 if (bridge->funcs->attach) { ret =3D bridge->funcs->attach(bridge, encoder, flags); @@ -594,7 +596,9 @@ int drm_bridge_attach(struct drm_encoder *encoder, stru= ct drm_bridge *bridge, err_reset_bridge: bridge->dev =3D NULL; bridge->encoder =3D NULL; + mutex_lock(&encoder->bridge_chain_mutex); list_del(&bridge->chain_node); + mutex_unlock(&encoder->bridge_chain_mutex); =20 if (ret !=3D -EPROBE_DEFER) DRM_ERROR("failed to attach bridge %pOF to encoder %s: %d\n", --=20 2.53.0 From nobody Thu Apr 2 22:05:40 2026 Received: from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56]) (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 EAEAB3D9DAB for ; Tue, 24 Mar 2026 08:58:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.84.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774342720; cv=none; b=pkBfogBh4fTOJWAqk+R6vhB1++qtDrk4p+U/pchCMMLfP3s2Mh+wfhpYgDW9hlQTd6+Ar3l8tG5HWIAv8BcVNAILRVxt9bfz7slaZlmmcGRjHlFqRPVfUMrFwdOG22T3UQUIStxONocwRiwyO/T3cpRL0QxCwzwI2EtlospK2OE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774342720; c=relaxed/simple; bh=lwuwQfNw50VzR/6IBSbeC326VmmhLTnPaNYatBbIM7w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HgZhr/LZsLhIINmmql0okYy3kUaeQmh2d+aCHNC9421tKZmYnokwsgKuIp7T31SJ2yakKDBreyATeANIadPa6PB3XZl0VWCC8rqy0+Zh3nzQ3d9VCNTg2iv9OskvPwQXsrTUQRJVhDlrrSbMbaYnIkcR/2K2+PCLScHZDzxhOjw= 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=DwzH36ci; arc=none smtp.client-ip=185.246.84.56 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="DwzH36ci" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id AB3F11A2FC2; Tue, 24 Mar 2026 08:58:37 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 7F5D76011D; Tue, 24 Mar 2026 08:58:37 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id AEAB210450FEB; Tue, 24 Mar 2026 09:58:33 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1774342716; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=1R1hfV70zQ5KkVzrmjTH8wbuM0KDVDayRY41Ek0UC1M=; b=DwzH36ciAxpiTJ+0cx3J96DXyNbwN/Ui1rBu1amDqqkttZtpE17Tlfs/bO6A9rmdjd9Yzk MXvNS4OsXZoTey3Ed6jyvqdC8gZadSwRxUG5fqua/u5Vv2ug7p6LDppZBL/NRaXsFmZHvH J0K8SKDnxvN/KXZE7BL+qJ32Q8qE5MDgT+tVb/x443kuyFELmqXTsNUs24369A24qOmRYT GPql25yYQDiTmumIOZqCUshwLYgQxqfLb9zsg4jbSkcro2aH+NPZjWNI45NQk2OkGlUYBf Bpq9X/J/h3b/bHv5hx7ICW3vkQ7eq7PJqihsi1C3fAqj10p+4YfiRQlOp/nQ5w== From: Luca Ceresoli Date: Tue, 24 Mar 2026 09:58:11 +0100 Subject: [PATCH v5 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: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-4-8bf786c5c7e6@bootlin.com> References: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@bootlin.com> In-Reply-To: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@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, Ian Ray , Luca Ceresoli 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 66ab89cf48aa..9aa10219ae36 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -1456,26 +1456,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 @@ -1485,14 +1496,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 @@ -1504,14 +1525,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.53.0 From nobody Thu Apr 2 22:05:40 2026 Received: from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56]) (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 334E83D9DD7 for ; Tue, 24 Mar 2026 08:58:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.84.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774342723; cv=none; b=LtYm2Boy7dzrWrN7fVQ6MFuO2+AORtmPBz+8SyVBsYV6hVUj4rOdd1wQYek1MUTjnhBnaoe92bAPRmWTz6oJLJUaNyCIw4/kcp0PUzkf0QzI3psONLDH9VF3B/x5u9I6XzvBcZzyYoiTRHEg0hOQTk8mE8BxefoxuBmHTNfSo9U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774342723; c=relaxed/simple; bh=pTn1kI7GaZJwfEy3QeOwCzyeAoTorItqLv2fj7/2FIk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Mfd7pCRJwO03xRsYuQEtLWTt3SprgW+bPh/vt+ikddXi4L4kt9+Bi7E1o6yy6lYzB3ueFBjmun90fABBSY3lCdEagbFQCPwFfdab87IRYMz5auWeu7UWNrKywhJkz8A0grPyxpzm8f8B7WrUHBhD2ljaPm4eNebVWrES1OUVX/Q= 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=OKw1YMxP; arc=none smtp.client-ip=185.246.84.56 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="OKw1YMxP" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 025441A2FC1; Tue, 24 Mar 2026 08:58:41 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id C689E6011D; Tue, 24 Mar 2026 08:58:40 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 151AA10451140; Tue, 24 Mar 2026 09:58:36 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1774342719; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=8mMcYEXUydZGS/Fj4aUMVLorw/18mDZTJVFYNGb7XMU=; b=OKw1YMxPQSbqPviE7lZb7Y5jieL3u4cLiz2S2nUo4eSx+5JokOgnDrZaIHHF0y02LT0mHt rss3/bSjgNYJsJ+aGjCOuWFEpeF0JBIbRxL4JwwT4OoO3UT3USGNerr3saWXm21Fcl7qDU nTDcc1SGM3YkqfobgsLsSywcHqYCtyjMrlTSYlon/km1jXQa4UEX6TdumtAHRYkUS7TaB5 UTXebyG1KF9x9inoeUzDC394kwqG3Mc2laPbc2+mgsYz865T0806zuHysqXZJIARDgIH7s VLByfDXLWRSIvsfDjS+f8jFDS2xaczRYlAUz5KSsMAYdKGbn2jRlJvokQInGOQ== From: Luca Ceresoli Date: Tue, 24 Mar 2026 09:58:12 +0100 Subject: [PATCH v5 5/7] drm/bridge: prevent encoder chain changes while iterating with list_for_each_entry_from() 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: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-5-8bf786c5c7e6@bootlin.com> References: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@bootlin.com> In-Reply-To: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@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, Ian Ray , Luca Ceresoli X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 These loops in drm_bridge.c iterate over the encoder chain using list_for_each_entry_from(), which does not prevent changes to the bridge chain while iterating over it. Convert most of those loops to instead use drm_for_each_bridge_in_chain_from(), which locks the chain. This also simplifies code. All the "simple" loops are converted here. The only ones not touched are those in drm_atomic_bridge_chain_pre_enable() and drm_atomic_bridge_chain_post_disable(), because they have nested loops which are not well handled by drm_for_each_bridge_in_chain_from(). Those two functions are handled by a separate commit. Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/drm_bridge.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 42ca4b243579..37feb8143de4 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -725,7 +725,7 @@ void drm_bridge_detach(struct drm_bridge *bridge) /** * drm_bridge_chain_mode_valid - validate the mode against all bridges in = the * encoder chain. - * @bridge: bridge control structure + * @first_bridge: bridge control structure * @info: display info against which the mode shall be validated * @mode: desired mode to be validated * @@ -739,17 +739,14 @@ void drm_bridge_detach(struct drm_bridge *bridge) * MODE_OK on success, drm_mode_status Enum error code on failure */ enum drm_mode_status -drm_bridge_chain_mode_valid(struct drm_bridge *bridge, +drm_bridge_chain_mode_valid(struct drm_bridge *first_bridge, const struct drm_display_info *info, const struct drm_display_mode *mode) { - struct drm_encoder *encoder; - - if (!bridge) + if (!first_bridge) return MODE_OK; =20 - encoder =3D bridge->encoder; - list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { + drm_for_each_bridge_in_chain_from(first_bridge, bridge) { enum drm_mode_status ret; =20 if (!bridge->funcs->mode_valid) @@ -767,7 +764,7 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_valid); /** * drm_bridge_chain_mode_set - set proposed mode for all bridges in the * encoder chain - * @bridge: bridge control structure + * @first_bridge: bridge control structure * @mode: desired mode to be set for the encoder chain * @adjusted_mode: updated mode that works for this encoder chain * @@ -776,20 +773,16 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_valid); * * Note: the bridge passed should be the one closest to the encoder */ -void drm_bridge_chain_mode_set(struct drm_bridge *bridge, +void drm_bridge_chain_mode_set(struct drm_bridge *first_bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) { - struct drm_encoder *encoder; - - if (!bridge) + if (!first_bridge) return; =20 - encoder =3D bridge->encoder; - list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { + drm_for_each_bridge_in_chain_from(first_bridge, bridge) if (bridge->funcs->mode_set) bridge->funcs->mode_set(bridge, mode, adjusted_mode); - } } EXPORT_SYMBOL(drm_bridge_chain_mode_set); =20 @@ -1013,7 +1006,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); =20 /** * drm_atomic_bridge_chain_enable - enables all bridges in the encoder cha= in - * @bridge: bridge control structure + * @first_bridge: bridge control structure * @state: atomic state being committed * * Calls &drm_bridge_funcs.atomic_enable (falls back on @@ -1023,22 +1016,18 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); * * Note: the bridge passed should be the one closest to the encoder */ -void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, +void drm_atomic_bridge_chain_enable(struct drm_bridge *first_bridge, struct drm_atomic_state *state) { - struct drm_encoder *encoder; - - if (!bridge) + if (!first_bridge) return; =20 - encoder =3D bridge->encoder; - list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { + drm_for_each_bridge_in_chain_from(first_bridge, bridge) if (bridge->funcs->atomic_enable) { bridge->funcs->atomic_enable(bridge, state); } else if (bridge->funcs->enable) { bridge->funcs->enable(bridge); } - } } EXPORT_SYMBOL(drm_atomic_bridge_chain_enable); =20 --=20 2.53.0 From nobody Thu Apr 2 22:05:40 2026 Received: from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56]) (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 74D443B3887 for ; Tue, 24 Mar 2026 08:58:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.84.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774342726; cv=none; b=t1Ii+X668rWMySssZ+8rOWk5YKQnOHDxeNStgVzwRGUQ4BzQ09SC4FfkaV3FO5tKNT86Br2tmOqN3lTFOvQTCA2zyULjOAzMFN/EXN66g8KOr/5T1sCu+27JpARHYyBfiWwaHNfMcuRcsg+zC6cDIYlB/7sXUwk7K5k1FH5IIWU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774342726; c=relaxed/simple; bh=kHYP3GKqaUar/Lser4mCyW9H14wxih6O+DcBGGRl4B4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tpVA2KmJmo1cvAXuxSm1eMmR50/Vy4HAwA0JVU1p3Niy/ewhIDr3n+auJEP2T5naUT6j+HKPel8hhQN5Cd7airIhX4H4BPK/aSBsNXFpINukcZrt27W2kSn067ddc930XBFhSxa1MeUto4xdD3v8tPnIckXgftKe9vn6e4SQins= 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=YrdDbir6; arc=none smtp.client-ip=185.246.84.56 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="YrdDbir6" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 28F551A2FC1; Tue, 24 Mar 2026 08:58:44 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id F377B6011D; Tue, 24 Mar 2026 08:58:43 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 5D8C11045102E; Tue, 24 Mar 2026 09:58:40 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1774342722; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=xkYRQmcUW+vz3mQLioPyVbL+TZwJhHqNYTwGvfKyZfE=; b=YrdDbir6GZlI1kcr4S2j6bOvWj3+3LGq6mg5uHOGYzBY44yHrZ45N4hH6mTZCvRI6Qviii 6iAIaZU3VswrTmcMwwG91MYqA8bHx1PETYHGp4D7MS/ObgPaoKHZpNlpfoTAWGwFbjiDdC iNfxGpYhINg2tpoEmvSOBOfSkCMG02ZjdpxY3RUvoo5d4JtaGtywIrKaqbmpM0cnF0XrVf 4nEjNSQ9N9Bqv2CVUv8uH8q3BxLRwACm8ZMu+uWeGZuv9h0H08aqnEADEbg6BQSwlD9e+t 166XcDmuGWSTFR/LKIxE5t52ZI5eDHPnXPKwnPJ5v+NHJcAuFrHmhLr6VMy2vg== From: Luca Ceresoli Date: Tue, 24 Mar 2026 09:58:13 +0100 Subject: [PATCH v5 6/7] drm/bridge: prevent encoder chain changes while iterating with list_for_each_entry_reverse() 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: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-6-8bf786c5c7e6@bootlin.com> References: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@bootlin.com> In-Reply-To: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@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, Ian Ray , Luca Ceresoli X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 These loops in drm_bridge.c iterate over the encoder chain using list_for_each_entry_reverse(), which does not prevent changes to the bridge chain while iterating over it. Take the encoder chain mutex while iterating to avoid chain changes while iterating. All the "simple" loops are converted. drm_atomic_bridge_chain_pre_enable() and drm_atomic_bridge_chain_post_disable() are handled by a separate commit. Signed-off-by: Luca Ceresoli --- Changes in v3: - Lock encoder->bridge_chain_mutex directly, no wrappers Changes in v2: none --- drivers/gpu/drm/drm_bridge.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 37feb8143de4..c76f3451af39 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -808,6 +808,7 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge = *bridge, return; =20 encoder =3D bridge->encoder; + mutex_lock(&encoder->bridge_chain_mutex); list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { if (iter->funcs->atomic_disable) { iter->funcs->atomic_disable(iter, state); @@ -818,6 +819,7 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge = *bridge, if (iter =3D=3D bridge) break; } + mutex_unlock(&encoder->bridge_chain_mutex); } EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); =20 @@ -1322,25 +1324,27 @@ int drm_atomic_bridge_chain_check(struct drm_bridge= *bridge, return ret; =20 encoder =3D bridge->encoder; - list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { - int ret; - - /* - * Bus flags are propagated by default. If a bridge needs to - * tweak the input bus flags for any reason, it should happen - * in its &drm_bridge_funcs.atomic_check() implementation such - * that preceding bridges in the chain can propagate the new - * bus flags. - */ - drm_atomic_bridge_propagate_bus_flags(iter, conn, - crtc_state->state); - - ret =3D drm_atomic_bridge_check(iter, crtc_state, conn_state); - if (ret) - return ret; + scoped_guard(mutex, &encoder->bridge_chain_mutex) { + list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { + int ret; + + /* + * Bus flags are propagated by default. If a bridge needs to + * tweak the input bus flags for any reason, it should happen + * in its &drm_bridge_funcs.atomic_check() implementation such + * that preceding bridges in the chain can propagate the new + * bus flags. + */ + drm_atomic_bridge_propagate_bus_flags(iter, conn, + crtc_state->state); + + ret =3D drm_atomic_bridge_check(iter, crtc_state, conn_state); + if (ret) + return ret; =20 - if (iter =3D=3D bridge) - break; + if (iter =3D=3D bridge) + break; + } } =20 return 0; --=20 2.53.0 From nobody Thu Apr 2 22:05:40 2026 Received: from smtpout-03.galae.net (smtpout-03.galae.net [185.246.85.4]) (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 64C023DA7F5 for ; Tue, 24 Mar 2026 08:58:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.85.4 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774342730; cv=none; b=cG6Dc7T0NvpytGwoVZNuu+cQCF08QdumpaPA06sIOFL2Kp+G+BXwURA7HQhiY0oucC3R1jIAos3oiRrZXiMRL3hmhksJqTbvwLagni2+SD/2rSqieA/5hsH4Ddf+wjee6qP2DKXM41oz535szl6Fm+4LFPPnQGC1NzJnQPi3/Dc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774342730; c=relaxed/simple; bh=u2TcTMSmt+bhHPWNClYJwZ5BNY0M3aWPFPMSUzKBuok=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Ji4RbU+0IFrmw4JcvM6vkyWOQFHM/gPMMq1wPGTrkFO4bKzK/vgkIZOJPpXHQbflzK53W7Lh9Uup8PbedJ8RVKOR6NU0rfl6C4H5fYk2HvZMn0Vw0m+rE87vaDGn8zs5hkRAYrmhiaRULw//VLKypieyrv4fmYg/G1P0Ho17suQ= 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=hpkP6J7/; arc=none smtp.client-ip=185.246.85.4 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="hpkP6J7/" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id B43C04E427C2; Tue, 24 Mar 2026 08:58:47 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 8AED06011D; Tue, 24 Mar 2026 08:58:47 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 7088310451136; Tue, 24 Mar 2026 09:58:43 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1774342726; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=KDcXJhmNEUUmoUs1F4dCWoL111Iv/TOQpcOGX7p6LwQ=; b=hpkP6J7/ceasAGc4roQHbAcxUkDzFIlRwwdfhixAyYTvs6CppC0BvruQIu3Em6CvB9JA8+ HXwDLh1JRHly2H5djOEEDsA3LTobz7Eywl7i2gXe6Ui78LQEQVh/5QRD0QSX9tppfcE6GM uu+flGJSL7cbWNuhutUOknqF8yW5E1J7VaPb0d776kXNZnrpqW8MQJE5V1FUczyPuzEPkg qzyj8qttT5/Psnd7hqlTAssYkJ/d1yQUuotde3jNU6hrerZ5vhkjaAUs3XcRv45wMdCa2c 2Gy2UpKvKO8QQF11NxNxNhu8pUKIidWXJXvL5qgxAWUGNF53OoLNjQUvpp0ZLA== From: Luca Ceresoli Date: Tue, 24 Mar 2026 09:58:14 +0100 Subject: [PATCH v5 7/7] drm/bridge: prevent encoder chain changes in pre_enable/post_disable 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: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-7-8bf786c5c7e6@bootlin.com> References: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@bootlin.com> In-Reply-To: <20260324-drm-bridge-alloc-encoder-chain-mutex-v5-0-8bf786c5c7e6@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, Ian Ray , Luca Ceresoli X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 Take the encoder chain mutex while iterating over the encoder chain in drm_atomic_bridge_chain_pre_enable() and drm_atomic_bridge_chain_post_disable() to ensure the lists won't change while being inspected. These functions have nested list_for_each_*() loops, which makes them complicated. list_for_each_entry_from() loops could be replaced by drm_for_each_bridge_in_chain_from(), but it would not work in a nested way in its current implementation. Besides, there is no "_reverse" variant of drm_for_each_bridge_in_chain_from(). Keep code simple and readable by explicitly locking around the outer loop. Thankfully there are no break or return points inside the loops, so the change is trivial and readable. Reviewed-by: Maxime Ripard Signed-off-by: Luca Ceresoli --- Changes in v3: - Lock encoder->bridge_chain_mutex directly, no wrappers - Improved commit message Changes in v2: - Improved commit message --- drivers/gpu/drm/drm_bridge.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index c76f3451af39..29231730e1f8 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -867,6 +867,7 @@ void drm_atomic_bridge_chain_post_disable(struct drm_br= idge *bridge, =20 encoder =3D bridge->encoder; =20 + mutex_lock(&encoder->bridge_chain_mutex); list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { limit =3D NULL; =20 @@ -915,6 +916,7 @@ void drm_atomic_bridge_chain_post_disable(struct drm_br= idge *bridge, /* Jump all bridges that we have already post_disabled */ bridge =3D limit; } + mutex_unlock(&encoder->bridge_chain_mutex); } EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); =20 @@ -961,6 +963,7 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_brid= ge *bridge, =20 encoder =3D bridge->encoder; =20 + mutex_lock(&encoder->bridge_chain_mutex); list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { if (iter->pre_enable_prev_first) { next =3D iter; @@ -1003,6 +1006,7 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_br= idge *bridge, if (iter =3D=3D bridge) break; } + mutex_unlock(&encoder->bridge_chain_mutex); } EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); =20 --=20 2.53.0