snd_dummy: Fix callout(9) races

Use callout_init_mtx(9) to associate the callback with the driver's
lock. Also make sure the callout is stopped properly during detach.

While here, introduce a dummy_active() function to know when it's
appropriate to stop or not reschedule the callout.

Sponsored by:	The FreeBSD Foundation
MFC after:	2 days
Reviewed by:	dev_submerge.ch, markj
Differential Revision:	https://reviews.freebsd.org/D47459
This commit is contained in:
Christos Margiolis 2024-11-26 15:48:02 +01:00
parent 56b7685ae3
commit 5bd08172b4

View File

@ -67,6 +67,24 @@ struct dummy_softc {
struct mtx *lock; struct mtx *lock;
}; };
static bool
dummy_active(struct dummy_softc *sc)
{
struct dummy_chan *ch;
int i;
snd_mtxassert(sc->lock);
for (i = 0; i < sc->chnum; i++) {
ch = &sc->chans[i];
if (ch->run)
return (true);
}
/* No channel is running at the moment. */
return (false);
}
static void static void
dummy_chan_io(void *arg) dummy_chan_io(void *arg)
{ {
@ -74,7 +92,9 @@ dummy_chan_io(void *arg)
struct dummy_chan *ch; struct dummy_chan *ch;
int i = 0; int i = 0;
snd_mtxlock(sc->lock); /* Do not reschedule if no channel is running. */
if (!dummy_active(sc))
return;
for (i = 0; i < sc->chnum; i++) { for (i = 0; i < sc->chnum; i++) {
ch = &sc->chans[i]; ch = &sc->chans[i];
@ -89,8 +109,6 @@ dummy_chan_io(void *arg)
snd_mtxlock(sc->lock); snd_mtxlock(sc->lock);
} }
callout_schedule(&sc->callout, 1); callout_schedule(&sc->callout, 1);
snd_mtxunlock(sc->lock);
} }
static int static int
@ -179,15 +197,15 @@ dummy_chan_trigger(kobj_t obj, void *data, int go)
switch (go) { switch (go) {
case PCMTRIG_START: case PCMTRIG_START:
if (!callout_active(&sc->callout))
callout_reset(&sc->callout, 1, dummy_chan_io, sc);
ch->ptr = 0; ch->ptr = 0;
ch->run = 1; ch->run = 1;
callout_reset(&sc->callout, 1, dummy_chan_io, sc);
break; break;
case PCMTRIG_STOP: case PCMTRIG_STOP:
case PCMTRIG_ABORT: case PCMTRIG_ABORT:
ch->run = 0; ch->run = 0;
if (callout_active(&sc->callout)) /* If all channels are stopped, stop the callout as well. */
if (!dummy_active(sc))
callout_stop(&sc->callout); callout_stop(&sc->callout);
default: default:
break; break;
@ -292,6 +310,7 @@ dummy_attach(device_t dev)
sc = device_get_softc(dev); sc = device_get_softc(dev);
sc->dev = dev; sc->dev = dev;
sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_dummy softc"); sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_dummy softc");
callout_init_mtx(&sc->callout, sc->lock, 0);
sc->cap_fmts[0] = SND_FORMAT(AFMT_S32_LE, 2, 0); sc->cap_fmts[0] = SND_FORMAT(AFMT_S32_LE, 2, 0);
sc->cap_fmts[1] = SND_FORMAT(AFMT_S24_LE, 2, 0); sc->cap_fmts[1] = SND_FORMAT(AFMT_S24_LE, 2, 0);
@ -316,7 +335,6 @@ dummy_attach(device_t dev)
if (pcm_register(dev, status)) if (pcm_register(dev, status))
return (ENXIO); return (ENXIO);
mixer_init(dev, &dummy_mixer_class, sc); mixer_init(dev, &dummy_mixer_class, sc);
callout_init(&sc->callout, 1);
return (0); return (0);
} }
@ -327,8 +345,8 @@ dummy_detach(device_t dev)
struct dummy_softc *sc = device_get_softc(dev); struct dummy_softc *sc = device_get_softc(dev);
int err; int err;
callout_drain(&sc->callout);
err = pcm_unregister(dev); err = pcm_unregister(dev);
callout_drain(&sc->callout);
snd_mtxfree(sc->lock); snd_mtxfree(sc->lock);
return (err); return (err);