git: d1dcb2b117a3 - main - snd_hdspe: Serialize rate and latency updates

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Mon, 02 Nov 2026 15:49:40 UTC
The branch main has been updated by kbowling:

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

commit d1dcb2b117a3c128c8e8eda047f93739f06d6099
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-07-21 03:33:08 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-11-02 15:49:22 +0000

    snd_hdspe: Serialize rate and latency updates
    
    Protect running-state checks and register updates with the softc lock.
    
    Drop the lock around sndbuf_resize(), which may sleep, and recheck the
    running state after reacquiring it.  Keep the resize ahead of the latency
    register update so that a failed resize leaves the hardware unchanged.
    
    Reviewed by:    br
    MFC after:      3 days
    Sponsored by:   BBOX.io
    Differential Revision:  https://reviews.freebsd.org/D58371
---
 sys/dev/sound/pci/hdspe-pcm.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/sys/dev/sound/pci/hdspe-pcm.c b/sys/dev/sound/pci/hdspe-pcm.c
index 1f32909ce0d3..017142949d62 100644
--- a/sys/dev/sound/pci/hdspe-pcm.c
+++ b/sys/dev/sound/pci/hdspe-pcm.c
@@ -838,6 +838,8 @@ hdspechan_setspeed(kobj_t obj, void *data, uint32_t speed)
 	device_printf(scp->dev, "hdspechan_setspeed(%d)\n", speed);
 #endif
 
+	mtx_lock(&sc->lock);
+
 	if (hdspe_running(sc) == 1)
 		goto end;
 
@@ -888,8 +890,10 @@ hdspechan_setspeed(kobj_t obj, void *data, uint32_t speed)
 
 	sc->speed = hr->speed;
 end:
+	speed = sc->speed;
+	mtx_unlock(&sc->lock);
 
-	return (sc->speed);
+	return (speed);
 }
 
 static uint32_t
@@ -911,6 +915,8 @@ hdspechan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 	device_printf(scp->dev, "hdspechan_setblocksize(%d)\n", blocksize);
 #endif
 
+	mtx_lock(&sc->lock);
+
 	if (hdspe_running(sc) == 1)
 		goto end;
 
@@ -941,12 +947,20 @@ hdspechan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 		}
 	}
 
+	/* sndbuf_resize() may sleep. */
+	mtx_unlock(&sc->lock);
 	if (sndbuf_resize(ch->buffer,
 	    (HDSPE_CHANBUF_SIZE * AFMT_CHANNEL(ch->format)) / (hl->period * 4),
 	    (hl->period * 4)) != 0)
 		return (0);
 
 	mtx_lock(&sc->lock);
+	/* A channel may have started while the lock was dropped. */
+	if (hdspe_running(sc) == 1) {
+		mtx_unlock(&sc->lock);
+		return (0);
+	}
+
 	sc->ctrl_register &= ~HDSPE_LAT_MASK;
 	sc->ctrl_register |= hdspe_encode_latency(hl->n);
 	hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register);
@@ -954,10 +968,12 @@ hdspechan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
 	mtx_unlock(&sc->lock);
 
 #if 0
-	device_printf(scp->dev, "New period=%d\n", sc->period);
+	device_printf(scp->dev, "New period=%d\n", hl->period);
 #endif
 
+	return (ch->buffer->blksz);
 end:
+	mtx_unlock(&sc->lock);
 
 	return (ch->buffer->blksz);
 }