git: 74db53d5d765 - main - snd_hdsp: Avoid allocation in the interrupt handler
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 22 Aug 2026 01:14:53 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=74db53d5d7657d0508940d1193f05a39df85434a
commit 74db53d5d7657d0508940d1193f05a39df85434a
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-22 01:13:12 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-22 01:13:26 +0000
snd_hdsp: Avoid allocation in the interrupt handler
Cache PCM children instead of calling device_get_children() from the
interrupt handler. Drain callbacks before child detach so cached
pointers cannot outlive the PCM softc. Allocate the parent softc by
its actual size.
This mirrors snd_hdspe's interrupt dispatch and detach lifecycle.
Reported by: christos
MFC after: 1 week
---
sys/dev/sound/pci/hdsp-pcm.c | 103 +++++++++++++++++++++++++++++++------------
sys/dev/sound/pci/hdsp.c | 26 +++++------
sys/dev/sound/pci/hdsp.h | 9 ++++
3 files changed, 96 insertions(+), 42 deletions(-)
diff --git a/sys/dev/sound/pci/hdsp-pcm.c b/sys/dev/sound/pci/hdsp-pcm.c
index 93ee576a3a67..b4df8ca90555 100644
--- a/sys/dev/sound/pci/hdsp-pcm.c
+++ b/sys/dev/sound/pci/hdsp-pcm.c
@@ -402,38 +402,21 @@ hdsp_running(struct sc_info *sc)
{
struct sc_pcminfo *scp;
struct sc_chinfo *ch;
- device_t *devlist;
- int devcount;
- int i, j;
- int running;
+ unsigned int i;
+ int j;
- running = 0;
-
- devlist = NULL;
- devcount = 0;
-
- if (device_get_children(sc->dev, &devlist, &devcount) != 0)
- running = 1; /* On error, avoid channel config changes. */
-
- for (i = 0; running == 0 && i < devcount; i++) {
- scp = device_get_ivars(devlist[i]);
+ for (i = 0; i < HDSP_MAX_PCMDEV; i++) {
+ scp = sc->pcms[i];
+ if (scp == NULL)
+ continue;
for (j = 0; j < scp->chnum; j++) {
ch = &scp->chan[j];
- if (ch->run) {
- running = 1;
- break;
- }
+ if (ch->run)
+ return (1);
}
}
-#if 0
- if (running == 1)
- device_printf(sc->dev, "hdsp is running\n");
-#endif
-
- free(devlist, M_TEMP);
-
- return (running);
+ return (0);
}
static void
@@ -1043,12 +1026,15 @@ hdsp_pcm_attach(device_t dev)
{
char status[SND_STATUSLEN];
struct sc_pcminfo *scp;
+ struct sc_info *sc;
const char *buf;
uint32_t pcm_flags;
int err;
int play, rec;
+ int i;
scp = device_get_ivars(dev);
+ sc = scp->sc;
scp->ih = &hdsp_pcm_intr;
if (scp->hc->ports & HDSP_CHAN_9632_ALL)
@@ -1086,8 +1072,8 @@ hdsp_pcm_attach(device_t dev)
}
snprintf(status, SND_STATUSLEN, "port 0x%jx irq %jd on %s",
- rman_get_start(scp->sc->cs),
- rman_get_start(scp->sc->irq),
+ rman_get_start(sc->cs),
+ rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
err = pcm_register(dev, status);
if (err) {
@@ -1097,19 +1083,78 @@ hdsp_pcm_attach(device_t dev)
mixer_init(dev, &hdspmixer_class, scp);
+ /* Register the PCM child for interrupt dispatch. */
+ mtx_lock(&sc->lock);
+ for (i = 0; i < HDSP_MAX_PCMDEV; i++) {
+ if (sc->pcms[i] == NULL) {
+ sc->pcms[i] = scp;
+ break;
+ }
+ }
+ mtx_unlock(&sc->lock);
+ if (i == HDSP_MAX_PCMDEV)
+ device_printf(dev, "Too many PCM children.\n");
+
return (0);
}
+static int
+hdsp_pcm_quiesce(struct sc_pcminfo *scp)
+{
+ struct sc_info *sc;
+ unsigned int i;
+ int slot;
+
+ sc = scp->sc;
+ slot = -1;
+ mtx_lock(&sc->lock);
+ for (i = 0; i < HDSP_MAX_PCMDEV; i++) {
+ if (sc->pcms[i] == scp) {
+ sc->pcm_detaching[i] = true;
+ while (sc->pcm_refs[i] != 0)
+ cv_wait(&sc->pcm_cv, &sc->lock);
+ slot = (int)i;
+ break;
+ }
+ }
+ mtx_unlock(&sc->lock);
+ return (slot);
+}
+
+static void
+hdsp_pcm_unquiesce(struct sc_pcminfo *scp, int slot, bool detach)
+{
+ struct sc_info *sc;
+
+ sc = scp->sc;
+ mtx_lock(&sc->lock);
+ KASSERT(slot >= 0 && slot < HDSP_MAX_PCMDEV &&
+ sc->pcms[slot] == scp && sc->pcm_detaching[slot],
+ ("invalid PCM slot %d", slot));
+ if (detach)
+ sc->pcms[slot] = NULL;
+ sc->pcm_detaching[slot] = false;
+ mtx_unlock(&sc->lock);
+}
+
static int
hdsp_pcm_detach(device_t dev)
{
- int err;
+ struct sc_pcminfo *scp;
+ int err, slot;
+
+ scp = device_get_ivars(dev);
+ slot = hdsp_pcm_quiesce(scp);
err = pcm_unregister(dev);
if (err) {
device_printf(dev, "Can't unregister device.\n");
+ if (slot >= 0)
+ hdsp_pcm_unquiesce(scp, slot, false);
return (err);
}
+ if (slot >= 0)
+ hdsp_pcm_unquiesce(scp, slot, true);
return (0);
}
diff --git a/sys/dev/sound/pci/hdsp.c b/sys/dev/sound/pci/hdsp.c
index 4fd3647edf51..b52b53231e50 100644
--- a/sys/dev/sound/pci/hdsp.c
+++ b/sys/dev/sound/pci/hdsp.c
@@ -101,11 +101,8 @@ hdsp_intr(void *p)
{
struct sc_pcminfo *scp;
struct sc_info *sc;
- device_t *devlist;
- int devcount;
+ unsigned int i;
int status;
- int err;
- int i;
sc = (struct sc_info *)p;
@@ -113,17 +110,18 @@ hdsp_intr(void *p)
status = hdsp_read_1(sc, HDSP_STATUS_REG);
if (status & HDSP_AUDIO_IRQ_PENDING) {
- if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
- return;
-
- for (i = 0; i < devcount; i++) {
- scp = device_get_ivars(devlist[i]);
- if (scp->ih != NULL)
- scp->ih(scp);
+ for (i = 0; i < HDSP_MAX_PCMDEV; i++) {
+ scp = sc->pcms[i];
+ if (scp == NULL || sc->pcm_detaching[i] ||
+ scp->ih == NULL)
+ continue;
+ sc->pcm_refs[i]++;
+ scp->ih(scp);
+ if (--sc->pcm_refs[i] == 0 && sc->pcm_detaching[i])
+ cv_broadcast(&sc->pcm_cv);
}
hdsp_write_1(sc, HDSP_INTERRUPT_ACK, 0);
- free(devlist, M_TEMP);
}
mtx_unlock(&sc->lock);
@@ -952,6 +950,7 @@ hdsp_attach(device_t dev)
"Analog input level ('LowGain', '+4dBU', '-10dBV')");
}
+ cv_init(&sc->pcm_cv, "snd_hdsp pcm");
bus_attach_children(dev);
return (0);
}
@@ -999,6 +998,7 @@ hdsp_detach(device_t dev)
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
if (sc->cs)
bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(0), sc->cs);
+ cv_destroy(&sc->pcm_cv);
mtx_destroy(&sc->lock);
return (0);
@@ -1015,7 +1015,7 @@ static device_method_t hdsp_methods[] = {
static driver_t hdsp_driver = {
"hdsp",
hdsp_methods,
- PCM_SOFTC_SIZE,
+ sizeof(struct sc_info),
};
DRIVER_MODULE(snd_hdsp, pci, hdsp_driver, 0, 0);
diff --git a/sys/dev/sound/pci/hdsp.h b/sys/dev/sound/pci/hdsp.h
index 18737709ec3f..81a0be7dc1e6 100644
--- a/sys/dev/sound/pci/hdsp.h
+++ b/sys/dev/sound/pci/hdsp.h
@@ -220,6 +220,9 @@ struct sc_pcminfo {
struct hdsp_channel *hc;
};
+/* Maximum number of PCM children. */
+#define HDSP_MAX_PCMDEV 4
+
/* HDSP device private data */
struct sc_info {
device_t dev;
@@ -249,6 +252,12 @@ struct sc_info {
uint32_t speed;
uint32_t force_period;
uint32_t force_speed;
+
+ /* PCM children used for interrupt dispatch. */
+ struct sc_pcminfo *pcms[HDSP_MAX_PCMDEV];
+ unsigned int pcm_refs[HDSP_MAX_PCMDEV];
+ bool pcm_detaching[HDSP_MAX_PCMDEV];
+ struct cv pcm_cv;
};
#define hdsp_read_1(sc, regno) \