diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c --- a/sys/dev/sound/pcm/sound.c +++ b/sys/dev/sound/pcm/sound.c @@ -674,23 +674,52 @@ return (err); } -static int -pcm_killchan(device_t dev) +static void +pcm_killchans(struct snddev_info *d) { - struct snddev_info *d = device_get_softc(dev); struct pcm_channel *ch; int error; + bool found; PCM_BUSYASSERT(d); + do { + found = false; + CHN_FOREACH(ch, d, channels.pcm) { + CHN_LOCK(ch); + /* + * We have to give a thread sleeping in chn_sleep() a + * chance to observe that the channel is dead. + */ + if ((ch->flags & CHN_F_SLEEPING) == 0) { + found = true; + CHN_UNLOCK(ch); + break; + } + /* + * Do not wait for the timeout in + * chn_read()/chn_write(). Wake up the sleeping thread + * and kill the channel. + */ + CHN_BROADCAST(&ch->intr_cv); + ch->flags |= CHN_F_DEAD; + CHN_UNLOCK(ch); + } - ch = CHN_FIRST(d, channels.pcm); + /* + * All channels are still sleeping. Sleep for a bit and try + * again to see if any of them is awake now. + */ + if (!found) { + pause_sbt("pcmkillchans", SBT_1MS * 5, 0, 0); + continue; + } - PCM_LOCK(d); - error = pcm_chn_remove(d, ch); - PCM_UNLOCK(d); - if (error) - return (error); - return (pcm_chn_destroy(ch)); + PCM_LOCK(d); + error = pcm_chn_remove(d, ch); + PCM_UNLOCK(d); + if (error == 0) + pcm_chn_destroy(ch); + } while (!CHN_EMPTY(d, channels.pcm)); } static int @@ -1000,15 +1029,6 @@ CHN_FOREACH(ch, d, channels.pcm) { CHN_LOCK(ch); - if (ch->flags & CHN_F_SLEEPING) { - /* - * We are detaching, so do not wait for the timeout in - * chn_read()/chn_write(). Wake up the thread and kill - * the channel immediately. - */ - CHN_BROADCAST(&ch->intr_cv); - ch->flags |= CHN_F_DEAD; - } chn_abort(ch); CHN_UNLOCK(ch); } @@ -1033,8 +1053,7 @@ dsp_destroy_dev(dev); (void)mixer_uninit(dev); - while (!CHN_EMPTY(d, channels.pcm)) - pcm_killchan(dev); + pcm_killchans(d); PCM_LOCK(d); PCM_RELEASE(d);