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)                                          
\

Reply via email to