diff options
| author | Goran Mekić <meka@tilda.center> | 2026-06-17 10:34:05 +0000 |
|---|---|---|
| committer | Christos Margiolis <christos@FreeBSD.org> | 2026-06-17 10:34:45 +0000 |
| commit | 47ae0a869c7db693ffb1ac058d63dcb79c4e68a8 (patch) | |
| tree | b527227968f9a1ec5966ed0c0c06a3eb33ad326c | |
| parent | 9889fac5b82c8de5b55c9ae708d57bc64c10c87a (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.c | 25 |
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); } |
