aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGoran Mekić <meka@tilda.center>2026-06-17 10:34:05 +0000
committerChristos Margiolis <christos@FreeBSD.org>2026-06-17 10:34:45 +0000
commit47ae0a869c7db693ffb1ac058d63dcb79c4e68a8 (patch)
treeb527227968f9a1ec5966ed0c0c06a3eb33ad326c
parent9889fac5b82c8de5b55c9ae708d57bc64c10c87a (diff)
sound: Start each channel individually
Unlock all members before starting any of them. Holding multiple channel locks while calling chn_start() on a virtual channel can trigger the parent, which acquires PCM_LOCK() while other virtual channels are still locked -- a lock order reversal. Reviewed by: christos Differential Revision: https://reviews.freebsd.org/D57399
-rw-r--r--sys/dev/sound/pcm/dsp.c25
1 files changed, 19 insertions, 6 deletions
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 05fdc18e31f8..1fa665b6b6cf 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -2742,16 +2742,29 @@ dsp_oss_syncstart(int sg_id)
/* Proceed only if no errors encountered. */
if (ret == 0) {
- /* Launch channels */
- while ((sm = SLIST_FIRST(&sg->members)) != NULL) {
- SLIST_REMOVE_HEAD(&sg->members, link);
+ /*
+ * Unlock all members before starting any of them.
+ * Holding multiple channel locks while calling chn_start()
+ * on a virtual channel can trigger the parent, which
+ * acquires PCM_LOCK() while other virtual channels are
+ * still locked -- a lock order reversal.
+ */
+ SLIST_FOREACH(sm, &sg->members, link) {
+ sm->ch->sm = NULL;
+ sm->ch->flags &= ~CHN_F_NOTRIGGER;
+ CHN_UNLOCK(sm->ch);
+ }
+ /*
+ * Start each channel individually, then remove it from
+ * the sync group and free its member structure.
+ */
+ while ((sm = SLIST_FIRST(&sg->members)) != NULL) {
c = sm->ch;
- c->sm = NULL;
+ CHN_LOCK(c);
chn_start(c, 1);
- c->flags &= ~CHN_F_NOTRIGGER;
CHN_UNLOCK(c);
-
+ SLIST_REMOVE_HEAD(&sg->members, link);
free(sm, M_DEVBUF);
}