mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-11-07 04:36:52 -10:00
snd_hdspe: Avoid allocation in the interrupt handler
Cache PCM children and drain interrupt callbacks before detach. Allocate the parent softc by its actual size. Reviewed by: br MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D58370
This commit is contained in:
1 parent
d383c870f7
commit
4c1004c00f
3 files changed
+94
-37
No files matched your search
@@ -391,35 +391,21 @@ hdspe_running(struct sc_info *sc)
|
||||
{
|
||||
struct sc_pcminfo *scp;
|
||||
struct sc_chinfo *ch;
|
||||
device_t *devlist;
|
||||
int devcount;
|
||||
int i, j;
|
||||
int err;
|
||||
unsigned int i;
|
||||
int j;
|
||||
|
||||
if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
|
||||
goto bad;
|
||||
|
||||
for (i = 0; i < devcount; i++) {
|
||||
scp = device_get_ivars(devlist[i]);
|
||||
for (i = 0; i < HDSPE_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)
|
||||
goto bad;
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
free(devlist, M_TEMP);
|
||||
|
||||
return (0);
|
||||
bad:
|
||||
|
||||
#if 0
|
||||
device_printf(sc->dev, "hdspe is running\n");
|
||||
#endif
|
||||
|
||||
free(devlist, M_TEMP);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1034,12 +1020,15 @@ hdspe_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 = &hdspe_pcm_intr;
|
||||
|
||||
if (scp->hc->ports & HDSPE_CHAN_AIO_ALL)
|
||||
@@ -1077,8 +1066,8 @@ hdspe_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) {
|
||||
@@ -1088,19 +1077,78 @@ hdspe_pcm_attach(device_t dev)
|
||||
|
||||
mixer_init(dev, &hdspemixer_class, scp);
|
||||
|
||||
/* Register the PCM child for interrupt dispatch. */
|
||||
mtx_lock(&sc->lock);
|
||||
for (i = 0; i < HDSPE_MAX_PCMDEV; i++) {
|
||||
if (sc->pcms[i] == NULL) {
|
||||
sc->pcms[i] = scp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mtx_unlock(&sc->lock);
|
||||
if (i == HDSPE_MAX_PCMDEV)
|
||||
device_printf(dev, "Too many PCM children.\n");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
hdspe_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 < HDSPE_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
|
||||
hdspe_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 < HDSPE_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
|
||||
hdspe_pcm_detach(device_t dev)
|
||||
{
|
||||
int err;
|
||||
struct sc_pcminfo *scp;
|
||||
int err, slot;
|
||||
|
||||
scp = device_get_ivars(dev);
|
||||
slot = hdspe_pcm_quiesce(scp);
|
||||
|
||||
err = pcm_unregister(dev);
|
||||
if (err) {
|
||||
device_printf(dev, "Can't unregister device.\n");
|
||||
if (slot >= 0)
|
||||
hdspe_pcm_unquiesce(scp, slot, false);
|
||||
return (err);
|
||||
}
|
||||
if (slot >= 0)
|
||||
hdspe_pcm_unquiesce(scp, slot, true);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
+13
-13
@@ -111,11 +111,8 @@ hdspe_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;
|
||||
|
||||
@@ -123,17 +120,18 @@ hdspe_intr(void *p)
|
||||
|
||||
status = hdspe_read_1(sc, HDSPE_STATUS_REG);
|
||||
if (status & HDSPE_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 < HDSPE_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);
|
||||
}
|
||||
|
||||
hdspe_write_1(sc, HDSPE_INTERRUPT_ACK, 0);
|
||||
free(devlist, M_TEMP);
|
||||
}
|
||||
|
||||
mtx_unlock(&sc->lock);
|
||||
@@ -844,6 +842,7 @@ hdspe_attach(device_t dev)
|
||||
"Analog input level ('LowGain', '+4dBU', '-10dBV')");
|
||||
}
|
||||
|
||||
cv_init(&sc->pcm_cv, "snd_hdspe pcm");
|
||||
bus_attach_children(dev);
|
||||
return (0);
|
||||
}
|
||||
@@ -891,6 +890,7 @@ hdspe_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);
|
||||
@@ -907,7 +907,7 @@ static device_method_t hdspe_methods[] = {
|
||||
static driver_t hdspe_driver = {
|
||||
"hdspe",
|
||||
hdspe_methods,
|
||||
PCM_SOFTC_SIZE,
|
||||
sizeof(struct sc_info),
|
||||
};
|
||||
|
||||
DRIVER_MODULE(snd_hdspe, pci, hdspe_driver, 0, 0);
|
||||
@@ -211,6 +211,9 @@ struct sc_pcminfo {
|
||||
struct hdspe_channel *hc;
|
||||
};
|
||||
|
||||
/* Maximum number of PCM children. */
|
||||
#define HDSPE_MAX_PCMDEV 8
|
||||
|
||||
/* HDSPe device private data */
|
||||
struct sc_info {
|
||||
device_t dev;
|
||||
@@ -241,6 +244,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[HDSPE_MAX_PCMDEV];
|
||||
unsigned int pcm_refs[HDSPE_MAX_PCMDEV];
|
||||
bool pcm_detaching[HDSPE_MAX_PCMDEV];
|
||||
struct cv pcm_cv;
|
||||
};
|
||||
|
||||
#define hdspe_read_1(sc, regno) \
|
||||
|
||||
Reference in new issue
Block a user