git: 776584319fb4 - main - sound: Fix lock order reversal in dsp_poll()

From: ShengYi Hung <aokblast_at_FreeBSD.org>
Date: Fri, 15 May 2026 14:53:18 UTC
The branch main has been updated by aokblast:

URL: https://cgit.FreeBSD.org/src/commit/?id=776584319fb4d66cdb1c2f91bed154dfe6a74e5e

commit 776584319fb4d66cdb1c2f91bed154dfe6a74e5e
Author:     ShengYi Hung <aokblast@FreeBSD.org>
AuthorDate: 2026-05-15 08:43:47 +0000
Commit:     ShengYi Hung <aokblast@FreeBSD.org>
CommitDate: 2026-05-15 14:52:48 +0000

    sound: Fix lock order reversal in dsp_poll()
    
    chn_poll() may hold both rdch and wrch channel locks while calling
    chn_trigger(rdch).  chn_trigger() switches the lock order from
    "channel -> dsp dev" to "dsp dev -> channel" by temporarily dropping
    the channel lock before acquiring the dsp lock.
    
    However, only rdch was unlocked during the transition while wrch
    remained locked. Since wrch is also a channel lock and witness had
    already established the lock order requirement:
    
        dsp dev -> channel
    
    witness reports a lock order reversal when pcm_lock() is acquired while
    wrch is still held.
    
    Avoid holding rdch and wrch simultaneously during chn_trigger()
    lock-order switching by only keeping the channel locks when needed.
    
    The issue can be reliably reproduced by starting pipewire,
    pipewire-pulse, and pavucontrol.
    
    Reviewed by:    christos
    MFC after:      2 weeks
    Sponsored by:   The FreeBSD Foundation
    Differential Revision: https://reviews.freebsd.org/D57009
---
 sys/dev/sound/pcm/dsp.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 797bfba81023..7bc1decc283b 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -1877,24 +1877,25 @@ dsp_poll(struct cdev *i_dev, int events, struct thread *td)
 
 	ret = 0;
 
-	dsp_lock_chans(priv, FREAD | FWRITE);
 	wrch = priv->wrch;
 	rdch = priv->rdch;
 
 	if (wrch != NULL && !(wrch->flags & CHN_F_DEAD)) {
+		CHN_LOCK(wrch);
 		e = (events & (POLLOUT | POLLWRNORM));
 		if (e)
 			ret |= chn_poll(wrch, e, td);
+		CHN_UNLOCK(wrch);
 	}
 
 	if (rdch != NULL && !(rdch->flags & CHN_F_DEAD)) {
+		CHN_LOCK(rdch);
 		e = (events & (POLLIN | POLLRDNORM));
 		if (e)
 			ret |= chn_poll(rdch, e, td);
+		CHN_UNLOCK(rdch);
 	}
 
-	dsp_unlock_chans(priv, FREAD | FWRITE);
-
 	PCM_GIANT_LEAVE(d);
 
 	return (ret);