Fix sound LOR problems:

dsp_open: rearrange to only hold one lock at a time

dsp_close: ditto

mixer_hwvol_init: delete locking, the only consumer seems to
be the ess driver and it only call it a creation time, I
think the device will be stable across the sleepable malloc.

cmi interrupt routine: Release locks while caller chn_intr,
either this or do what emu10k1 does which is have no locks
at in the interrupt handler.

Submitted by:	mat@cnd.mcgill.ca
This commit is contained in:
Scott Long 2003-11-11 05:38:28 +00:00
parent 5274183ef4
commit 42c3555cff
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=122461
3 changed files with 123 additions and 118 deletions

View File

@ -516,41 +516,41 @@ cmi_intr(void *data)
{
struct sc_info *sc = data;
u_int32_t intrstat;
u_int32_t toclear;
snd_mtxlock(sc->lock);
intrstat = cmi_rd(sc, CMPCI_REG_INTR_STATUS, 4);
if ((intrstat & CMPCI_REG_ANY_INTR) == 0) {
goto out;
}
if ((intrstat & CMPCI_REG_ANY_INTR) != 0) {
/* Disable interrupts */
if (intrstat & CMPCI_REG_CH0_INTR) {
cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE);
}
toclear = 0;
if (intrstat & CMPCI_REG_CH0_INTR) {
toclear |= CMPCI_REG_CH0_INTR_ENABLE;
//cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE);
}
if (intrstat & CMPCI_REG_CH1_INTR) {
cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE);
}
if (intrstat & CMPCI_REG_CH1_INTR) {
toclear |= CMPCI_REG_CH1_INTR_ENABLE;
//cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE);
}
/* Signal interrupts to channel */
if (intrstat & CMPCI_REG_CH0_INTR) {
chn_intr(sc->pch.channel);
}
if (toclear) {
cmi_clr4(sc, CMPCI_REG_INTR_CTRL, toclear);
snd_mtxunlock(sc->lock);
if (intrstat & CMPCI_REG_CH1_INTR) {
chn_intr(sc->rch.channel);
}
/* Signal interrupts to channel */
if (intrstat & CMPCI_REG_CH0_INTR) {
chn_intr(sc->pch.channel);
}
/* Enable interrupts */
if (intrstat & CMPCI_REG_CH0_INTR) {
cmi_set4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE);
}
if (intrstat & CMPCI_REG_CH1_INTR) {
chn_intr(sc->rch.channel);
}
if (intrstat & CMPCI_REG_CH1_INTR) {
cmi_set4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE);
}
snd_mtxlock(sc->lock);
cmi_set4(sc, CMPCI_REG_INTR_CTRL, toclear);
out:
}
}
snd_mtxunlock(sc->lock);
return;
}

View File

@ -172,6 +172,8 @@ dsp_open(dev_t i_dev, int flags, int mode, struct thread *td)
intrmask_t s;
u_int32_t fmt;
int devtype;
int rdref;
int error;
s = spltty();
d = dsp_get_info(i_dev);
@ -207,6 +209,8 @@ dsp_open(dev_t i_dev, int flags, int mode, struct thread *td)
panic("impossible devtype %d", devtype);
}
rdref = 0;
/* lock snddev so nobody else can monkey with it */
pcm_lock(d);
@ -249,67 +253,66 @@ dsp_open(dev_t i_dev, int flags, int mode, struct thread *td)
return EBUSY;
}
/* got a channel, already locked for us */
if (chn_reset(rdch, fmt)) {
pcm_chnrelease(rdch);
i_dev->si_drv1 = NULL;
pcm_unlock(d);
splx(s);
return ENODEV;
}
if (flags & O_NONBLOCK)
rdch->flags |= CHN_F_NBIO;
pcm_chnref(rdch, 1);
CHN_UNLOCK(rdch);
rdref = 1;
/*
* Record channel created, ref'ed and unlocked
*/
}
if (flags & FWRITE) {
/* open for write */
wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1);
if (!wrch) {
/* no channel available */
if (flags & FREAD) {
/* just opened a read channel, release it */
pcm_chnrelease(rdch);
}
/* exit */
pcm_unlock(d);
splx(s);
return EBUSY;
/* open for write */
wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1);
error = 0;
if (!wrch)
error = EBUSY; /* XXX Right return code? */
else if (chn_reset(wrch, fmt))
error = ENODEV;
if (error != 0) {
if (wrch) {
/*
* Free play channel
*/
pcm_chnrelease(wrch);
i_dev->si_drv2 = NULL;
}
/* got a channel, already locked for us */
if (rdref) {
/*
* Lock, deref and release previously created record channel
*/
CHN_LOCK(rdch);
pcm_chnref(rdch, -1);
pcm_chnrelease(rdch);
i_dev->si_drv1 = NULL;
}
pcm_unlock(d);
splx(s);
return error;
}
if (flags & O_NONBLOCK)
wrch->flags |= CHN_F_NBIO;
pcm_chnref(wrch, 1);
CHN_UNLOCK(wrch);
}
i_dev->si_drv1 = rdch;
i_dev->si_drv2 = wrch;
/* Bump refcounts, reset and unlock any channels that we just opened,
* and then release device lock.
*/
if (flags & FREAD) {
if (chn_reset(rdch, fmt)) {
pcm_chnrelease(rdch);
i_dev->si_drv1 = NULL;
if (wrch && (flags & FWRITE)) {
pcm_chnrelease(wrch);
i_dev->si_drv2 = NULL;
}
pcm_unlock(d);
splx(s);
return ENODEV;
}
if (flags & O_NONBLOCK)
rdch->flags |= CHN_F_NBIO;
pcm_chnref(rdch, 1);
CHN_UNLOCK(rdch);
}
if (flags & FWRITE) {
if (chn_reset(wrch, fmt)) {
pcm_chnrelease(wrch);
i_dev->si_drv2 = NULL;
if (flags & FREAD) {
CHN_LOCK(rdch);
pcm_chnref(rdch, -1);
pcm_chnrelease(rdch);
i_dev->si_drv1 = NULL;
}
pcm_unlock(d);
splx(s);
return ENODEV;
}
if (flags & O_NONBLOCK)
wrch->flags |= CHN_F_NBIO;
pcm_chnref(wrch, 1);
CHN_UNLOCK(wrch);
}
pcm_unlock(d);
splx(s);
return 0;
@ -321,7 +324,7 @@ dsp_close(dev_t i_dev, int flags, int mode, struct thread *td)
struct pcm_channel *rdch, *wrch;
struct snddev_info *d;
intrmask_t s;
int exit;
int refs;
s = spltty();
d = dsp_get_info(i_dev);
@ -329,53 +332,57 @@ dsp_close(dev_t i_dev, int flags, int mode, struct thread *td)
rdch = i_dev->si_drv1;
wrch = i_dev->si_drv2;
exit = 0;
refs = 0;
/* decrement refcount for each channel, exit if nonzero */
if (rdch) {
CHN_LOCK(rdch);
if (pcm_chnref(rdch, -1) > 0) {
CHN_UNLOCK(rdch);
exit = 1;
}
refs += pcm_chnref(rdch, -1);
CHN_UNLOCK(rdch);
}
if (wrch) {
CHN_LOCK(wrch);
if (pcm_chnref(wrch, -1) > 0) {
CHN_UNLOCK(wrch);
exit = 1;
}
refs += pcm_chnref(wrch, -1);
CHN_UNLOCK(wrch);
}
if (exit) {
/*
* If there are no more references, release the channels.
*/
if ((rdch || wrch) && refs == 0) {
if (pcm_getfakechan(d))
pcm_getfakechan(d)->flags = 0;
i_dev->si_drv1 = NULL;
i_dev->si_drv2 = NULL;
dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
pcm_unlock(d);
splx(s);
return 0;
}
/* both refcounts are zero, abort and release */
if (pcm_getfakechan(d))
pcm_getfakechan(d)->flags = 0;
i_dev->si_drv1 = NULL;
i_dev->si_drv2 = NULL;
dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
pcm_unlock(d);
if (rdch) {
chn_abort(rdch); /* won't sleep */
rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
chn_reset(rdch, 0);
pcm_chnrelease(rdch);
}
if (wrch) {
chn_flush(wrch); /* may sleep */
wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
chn_reset(wrch, 0);
pcm_chnrelease(wrch);
}
if (rdch) {
CHN_LOCK(rdch);
chn_abort(rdch); /* won't sleep */
rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
chn_reset(rdch, 0);
pcm_chnrelease(rdch);
}
if (wrch) {
CHN_LOCK(wrch);
/*
* XXX: Maybe the right behaviour is to abort on non_block.
* It seems that mplayer flushes the audio queue by quickly
* closing and re-opening. In FBSD, there's a long pause
* while the audio queue flushes that I presume isn't there in
* linux.
*/
chn_flush(wrch); /* may sleep */
wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
chn_reset(wrch, 0);
pcm_chnrelease(wrch);
}
} else
pcm_unlock(d);
splx(s);
return 0;
}

View File

@ -319,7 +319,6 @@ mixer_hwvol_init(device_t dev)
pdev = mixer_get_devt(dev);
m = pdev->si_drv1;
snd_mtxlock(m->lock);
m->hwvol_mixer = SOUND_MIXER_VOLUME;
m->hwvol_step = 5;
@ -330,7 +329,6 @@ mixer_hwvol_init(device_t dev)
OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
sysctl_hw_snd_hwvol_mixer, "A", "");
#endif
snd_mtxunlock(m->lock);
return 0;
}