Lock channels only as necessary in dsp_ioctl(), and only lock one

channel at a time unless it is actually necessary to lock both.
This avoids problems with lock order reversal and malloc() calls
with a mutex held when lower level code unlocks a channel, calls malloc(),
and relocks the channel.  This also avoids the cost of some  unnecessary
locking and unlocking.

Tested by:	matk
This commit is contained in:
truckman 2004-02-28 19:42:48 +00:00
parent 1843690ef3
commit 11e768d9c0

View File

@ -444,7 +444,7 @@ dsp_write(dev_t i_dev, struct uio *buf, int flag)
static int
dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
{
struct pcm_channel *wrch, *rdch;
struct pcm_channel *chn, *rdch, *wrch;
struct snddev_info *d;
intrmask_t s;
int kill;
@ -477,22 +477,19 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
if (kill & 2)
rdch = NULL;
if (rdch != NULL)
CHN_LOCK(rdch);
if (wrch != NULL)
CHN_LOCK(wrch);
switch(cmd) {
#ifdef OLDPCM_IOCTL
/*
* we start with the new ioctl interface.
*/
case AIONWRITE: /* how many bytes can write ? */
CHN_LOCK(wrch);
/*
if (wrch && wrch->bufhard.dl)
while (chn_wrfeed(wrch) == 0);
*/
*arg_i = wrch? sndbuf_getfree(wrch->bufsoft) : 0;
CHN_UNLOCK(wrch);
break;
case AIOSSIZE: /* set the current blocksize */
@ -502,12 +499,16 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
p->play_size = 0;
p->rec_size = 0;
if (wrch) {
CHN_LOCK(wrch);
chn_setblocksize(wrch, 2, p->play_size);
p->play_size = sndbuf_getblksz(wrch->bufsoft);
CHN_UNLOCK(wrch);
}
if (rdch) {
CHN_LOCK(rdch);
chn_setblocksize(rdch, 2, p->rec_size);
p->rec_size = sndbuf_getblksz(rdch->bufsoft);
CHN_UNLOCK(rdch);
}
}
break;
@ -515,36 +516,50 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
{
struct snd_size *p = (struct snd_size *)arg;
if (wrch)
if (wrch) {
CHN_LOCK(wrch);
p->play_size = sndbuf_getblksz(wrch->bufsoft);
if (rdch)
CHN_UNLOCK(wrch);
}
if (rdch) {
CHN_LOCK(rdch);
p->rec_size = sndbuf_getblksz(rdch->bufsoft);
CHN_UNLOCK(rdch);
}
}
break;
case AIOSFMT:
{
snd_chan_param *p = (snd_chan_param *)arg;
if (wrch) {
chn_setformat(wrch, p->play_format);
chn_setspeed(wrch, p->play_rate);
}
if (rdch) {
chn_setformat(rdch, p->rec_format);
chn_setspeed(rdch, p->rec_rate);
}
}
/* FALLTHROUGH */
case AIOGFMT:
{
snd_chan_param *p = (snd_chan_param *)arg;
p->play_rate = wrch? wrch->speed : 0;
p->rec_rate = rdch? rdch->speed : 0;
p->play_format = wrch? wrch->format : 0;
p->rec_format = rdch? rdch->format : 0;
if (wrch) {
CHN_LOCK(wrch);
if (cmd == AIOSFMT) {
chn_setformat(wrch, p->play_format);
chn_setspeed(wrch, p->play_rate);
}
p->play_rate = wrch->speed;
p->play_format = wrch->format;
CHN_UNLOCK(wrch);
} else {
p->play_rate = 0;
p->play_format = 0;
}
if (rdch) {
CHN_LOCK(rdch);
if (cmd == AIOSFMT) {
chn_setformat(rdch, p->rec_format);
chn_setspeed(rdch, p->rec_rate);
}
p->rec_rate = rdch->speed;
p->rec_format = rdch->format;
CHN_UNLOCK(rdch);
} else {
p->rec_rate = 0;
p->rec_format = 0;
}
}
break;
@ -554,10 +569,14 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
dev_t pdev;
if (rdch)
if (rdch) {
CHN_LOCK(rdch);
rcaps = chn_getcaps(rdch);
if (wrch)
}
if (wrch) {
CHN_LOCK(wrch);
pcaps = chn_getcaps(wrch);
}
p->rate_min = max(rcaps? rcaps->minspeed : 0,
pcaps? pcaps->minspeed : 0);
p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
@ -573,15 +592,23 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
p->mixers = 1; /* default: one mixer */
p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
p->left = p->right = 100;
if (rdch)
CHN_UNLOCK(rdch);
if (wrch)
CHN_UNLOCK(wrch);
}
break;
case AIOSTOP:
if (*arg_i == AIOSYNC_PLAY && wrch)
if (*arg_i == AIOSYNC_PLAY && wrch) {
CHN_LOCK(wrch);
*arg_i = chn_abort(wrch);
else if (*arg_i == AIOSYNC_CAPTURE && rdch)
CHN_UNLOCK(wrch);
} else if (*arg_i == AIOSYNC_CAPTURE && rdch) {
CHN_LOCK(rdch);
*arg_i = chn_abort(rdch);
else {
CHN_UNLOCK(rdch);
} else {
printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
*arg_i = 0;
}
@ -596,9 +623,15 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
* here follow the standard ioctls (filio.h etc.)
*/
case FIONREAD: /* get # bytes to read */
/* if (rdch && rdch->bufhard.dl)
while (chn_rdfeed(rdch) == 0);
*/ *arg_i = rdch? sndbuf_getready(rdch->bufsoft) : 0;
if (rdch) {
CHN_LOCK(rdch);
/* if (rdch && rdch->bufhard.dl)
while (chn_rdfeed(rdch) == 0);
*/
*arg_i = sndbuf_getready(rdch->bufsoft);
CHN_UNLOCK(rdch);
} else
*arg_i = 0;
break;
case FIOASYNC: /*set/clear async i/o */
@ -607,15 +640,21 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
case SNDCTL_DSP_NONBLOCK:
case FIONBIO: /* set/clear non-blocking i/o */
if (rdch)
rdch->flags &= ~CHN_F_NBIO;
if (wrch)
wrch->flags &= ~CHN_F_NBIO;
if (*arg_i) {
if (rdch)
if (rdch) {
CHN_LOCK(rdch);
if (*arg_i)
rdch->flags |= CHN_F_NBIO;
if (wrch)
else
rdch->flags &= ~CHN_F_NBIO;
CHN_UNLOCK(rdch);
}
if (wrch) {
CHN_LOCK(wrch);
if (*arg_i)
wrch->flags |= CHN_F_NBIO;
else
wrch->flags &= ~CHN_F_NBIO;
CHN_UNLOCK(wrch);
}
break;
@ -625,71 +664,93 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
#define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
case SNDCTL_DSP_GETBLKSIZE:
if (wrch)
*arg_i = sndbuf_getblksz(wrch->bufsoft);
else if (rdch)
*arg_i = sndbuf_getblksz(rdch->bufsoft);
else
*arg_i = 0;
chn = wrch ? wrch : rdch;
CHN_LOCK(chn);
*arg_i = sndbuf_getblksz(chn->bufsoft);
CHN_UNLOCK(chn);
break ;
case SNDCTL_DSP_SETBLKSIZE:
RANGE(*arg_i, 16, 65536);
if (wrch)
if (wrch) {
CHN_LOCK(wrch);
chn_setblocksize(wrch, 2, *arg_i);
if (rdch)
CHN_UNLOCK(wrch);
}
if (rdch) {
CHN_LOCK(rdch);
chn_setblocksize(rdch, 2, *arg_i);
CHN_UNLOCK(rdch);
}
break;
case SNDCTL_DSP_RESET:
DEB(printf("dsp reset\n"));
if (wrch) {
CHN_LOCK(wrch);
chn_abort(wrch);
chn_resetbuf(wrch);
CHN_UNLOCK(wrch);
}
if (rdch) {
CHN_LOCK(rdch);
chn_abort(rdch);
chn_resetbuf(rdch);
CHN_UNLOCK(rdch);
}
break;
case SNDCTL_DSP_SYNC:
DEB(printf("dsp sync\n"));
/* chn_sync may sleep */
if (wrch)
if (wrch) {
CHN_LOCK(wrch);
chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
CHN_UNLOCK(wrch);
}
break;
case SNDCTL_DSP_SPEED:
/* chn_setspeed may sleep */
tmp = 0;
if (wrch) {
CHN_LOCK(wrch);
ret = chn_setspeed(wrch, *arg_i);
tmp = wrch->speed;
CHN_UNLOCK(wrch);
}
if (rdch && ret == 0) {
CHN_LOCK(rdch);
ret = chn_setspeed(rdch, *arg_i);
if (tmp == 0)
tmp = rdch->speed;
CHN_UNLOCK(rdch);
}
*arg_i = tmp;
break;
case SOUND_PCM_READ_RATE:
*arg_i = wrch? wrch->speed : rdch->speed;
chn = wrch ? wrch : rdch;
CHN_LOCK(chn);
*arg_i = chn->speed;
CHN_UNLOCK(chn);
break;
case SNDCTL_DSP_STEREO:
tmp = -1;
*arg_i = (*arg_i)? AFMT_STEREO : 0;
if (wrch) {
CHN_LOCK(wrch);
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
CHN_UNLOCK(wrch);
}
if (rdch && ret == 0) {
CHN_LOCK(rdch);
ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
if (tmp == -1)
tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
CHN_UNLOCK(rdch);
}
*arg_i = tmp;
break;
@ -700,47 +761,67 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
tmp = 0;
*arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
if (wrch) {
CHN_LOCK(wrch);
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
CHN_UNLOCK(wrch);
}
if (rdch && ret == 0) {
CHN_LOCK(rdch);
ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
if (tmp == 0)
tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
CHN_UNLOCK(rdch);
}
*arg_i = tmp;
} else
*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
} else {
chn = wrch ? wrch : rdch;
CHN_LOCK(chn);
*arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
CHN_UNLOCK(chn);
}
break;
case SOUND_PCM_READ_CHANNELS:
*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1;
chn = wrch ? wrch : rdch;
CHN_LOCK(chn);
*arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
CHN_UNLOCK(chn);
break;
case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */
*arg_i = wrch? chn_getformats(wrch) : chn_getformats(rdch);
chn = wrch ? wrch : rdch;
CHN_LOCK(chn);
*arg_i = chn_getformats(chn);
CHN_UNLOCK(chn);
break ;
case SNDCTL_DSP_SETFMT: /* sets _one_ format */
/* XXX locking */
if ((*arg_i != AFMT_QUERY)) {
tmp = 0;
if (wrch) {
CHN_LOCK(wrch);
ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
tmp = wrch->format & ~AFMT_STEREO;
CHN_UNLOCK(wrch);
}
if (rdch && ret == 0) {
CHN_LOCK(rdch);
ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
if (tmp == 0)
tmp = rdch->format & ~AFMT_STEREO;
CHN_UNLOCK(rdch);
}
*arg_i = tmp;
} else
*arg_i = (wrch? wrch->format : rdch->format) & ~AFMT_STEREO;
} else {
chn = wrch ? wrch : rdch;
CHN_LOCK(chn);
*arg_i = chn->format & ~AFMT_STEREO;
CHN_UNLOCK(chn);
}
break;
case SNDCTL_DSP_SETFRAGMENT:
/* XXX locking */
DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
{
u_int32_t fragln = (*arg_i) & 0x0000ffff;
@ -759,14 +840,18 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
if (rdch) {
CHN_LOCK(rdch);
ret = chn_setblocksize(rdch, maxfrags, fragsz);
maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
fragsz = sndbuf_getblksz(rdch->bufsoft);
CHN_UNLOCK(rdch);
}
if (wrch && ret == 0) {
CHN_LOCK(wrch);
ret = chn_setblocksize(wrch, maxfrags, fragsz);
maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
fragsz = sndbuf_getblksz(wrch->bufsoft);
CHN_UNLOCK(wrch);
}
fragln = 0;
@ -785,10 +870,12 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
if (rdch) {
struct snd_dbuf *bs = rdch->bufsoft;
CHN_LOCK(rdch);
a->bytes = sndbuf_getready(bs);
a->fragments = a->bytes / sndbuf_getblksz(bs);
a->fragstotal = sndbuf_getblkcnt(bs);
a->fragsize = sndbuf_getblksz(bs);
CHN_UNLOCK(rdch);
}
}
break;
@ -800,11 +887,13 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
if (wrch) {
struct snd_dbuf *bs = wrch->bufsoft;
CHN_LOCK(wrch);
chn_wrupdate(wrch);
a->bytes = sndbuf_getfree(bs);
a->fragments = a->bytes / sndbuf_getblksz(bs);
a->fragstotal = sndbuf_getblkcnt(bs);
a->fragsize = sndbuf_getblksz(bs);
CHN_UNLOCK(wrch);
}
}
break;
@ -815,11 +904,13 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
if (rdch) {
struct snd_dbuf *bs = rdch->bufsoft;
CHN_LOCK(rdch);
chn_rdupdate(rdch);
a->bytes = sndbuf_gettotal(bs);
a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
a->ptr = sndbuf_getreadyptr(bs);
rdch->blocks = sndbuf_getblocks(bs);
CHN_UNLOCK(rdch);
} else
ret = EINVAL;
}
@ -831,11 +922,13 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
if (wrch) {
struct snd_dbuf *bs = wrch->bufsoft;
CHN_LOCK(wrch);
chn_wrupdate(wrch);
a->bytes = sndbuf_gettotal(bs);
a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
a->ptr = sndbuf_getreadyptr(bs);
wrch->blocks = sndbuf_getblocks(bs);
CHN_UNLOCK(wrch);
} else
ret = EINVAL;
}
@ -848,32 +941,47 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
break;
case SOUND_PCM_READ_BITS:
*arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8;
chn = wrch ? wrch : rdch;
CHN_LOCK(chn);
*arg_i = (chn->format & AFMT_16BIT) ? 16 : 8;
CHN_UNLOCK(chn);
break;
case SNDCTL_DSP_SETTRIGGER:
if (rdch) {
CHN_LOCK(rdch);
rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
if (*arg_i & PCM_ENABLE_INPUT)
chn_start(rdch, 1);
else
rdch->flags |= CHN_F_NOTRIGGER;
CHN_UNLOCK(rdch);
}
if (wrch) {
CHN_LOCK(wrch);
wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
if (*arg_i & PCM_ENABLE_OUTPUT)
chn_start(wrch, 1);
else
wrch->flags |= CHN_F_NOTRIGGER;
CHN_UNLOCK(wrch);
}
break;
case SNDCTL_DSP_GETTRIGGER:
*arg_i = 0;
if (wrch && wrch->flags & CHN_F_TRIGGERED)
*arg_i |= PCM_ENABLE_OUTPUT;
if (rdch && rdch->flags & CHN_F_TRIGGERED)
*arg_i |= PCM_ENABLE_INPUT;
if (wrch) {
CHN_LOCK(wrch);
if (wrch->flags & CHN_F_TRIGGERED)
*arg_i |= PCM_ENABLE_OUTPUT;
CHN_UNLOCK(wrch);
}
if (rdch) {
CHN_LOCK(rdch);
if (rdch->flags & CHN_F_TRIGGERED)
*arg_i |= PCM_ENABLE_INPUT;
CHN_UNLOCK(rdch);
}
break;
case SNDCTL_DSP_GETODELAY:
@ -881,16 +989,20 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
struct snd_dbuf *b = wrch->bufhard;
struct snd_dbuf *bs = wrch->bufsoft;
CHN_LOCK(wrch);
chn_wrupdate(wrch);
*arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
CHN_UNLOCK(wrch);
} else
ret = EINVAL;
break;
case SNDCTL_DSP_POST:
if (wrch) {
CHN_LOCK(wrch);
wrch->flags &= ~CHN_F_NOTRIGGER;
chn_start(wrch, 1);
CHN_UNLOCK(wrch);
}
break;
@ -908,10 +1020,6 @@ dsp_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
ret = EINVAL;
break;
}
if (rdch != NULL)
CHN_UNLOCK(rdch);
if (wrch != NULL)
CHN_UNLOCK(wrch);
relchns(i_dev, rdch, wrch, 0);
splx(s);
return ret;