git: 74db53d5d765 - main - snd_hdsp: Avoid allocation in the interrupt handler
Kevin Bowling <[email protected]>
| Newsgroups | gmane.os.freebsd.devel.cvs.src,gmane.os.freebsd.current.scm |
|---|---|
| Message-ID | <[email protected]> |
The branch main has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=74db53d5d7657d0508940d1193f05a39df85434a commit 74db53d5d7657d0508940d1193f05a39df85434a Author: Kevin Bowling <[email protected]> AuthorDate: 2026-08-22 01:13:12 +0000 Commit: Kevin Bowling <[email protected]> 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) \