5.2.1-RC hangs occasionally when sound files are played using
the pcm driver
Don Lewis
truckman at FreeBSD.org
Mon Feb 23 21:44:10 PST 2004
On 23 Feb, Brian O'Shea wrote:
> --- Don Lewis <truckman at FreeBSD.org> wrote:
>>
>> The cause of deadlocks is more likely to be caught by WITNESS. In this
>
> With WITNESS the hang still occurrs, and still no panic. It's hard to
> tell since the problem tends to happen at random times, but it seems like
> it happens more quickly with the kernel that has WITNESS and INVARIANTS
> enabled.
Can you see any kernel diagnostic messages, or are you running X? If
you are running X, can you set up a serial console, or can you reproduce
the hang while switched to a text console?
Depending on the cause of the problem, you might need to build a kernel
with DDB support and break into DDB from the console while the machine
is hung to figure out the cause.
>> case it might be the result of a malloc() call while a mutex is held.
>> Even the version of the sound code in the most recent -CURRENT has some
>> problems in this area. I've got a patch out for testing that will this
>> problem to some extent, but it might not be enough.
>>
>
> I'd be willing to test your patch, if that would help. Where can I
> find it?
The patch below should be applied to a recent version of -CURRENT.
Index: sys/dev/sound/pcm/dsp.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/sound/pcm/dsp.c,v
retrieving revision 1.73
diff -u -r1.73 dsp.c
--- sys/dev/sound/pcm/dsp.c 21 Feb 2004 21:10:47 -0000 1.73
+++ sys/dev/sound/pcm/dsp.c 22 Feb 2004 23:11:43 -0000
@@ -444,7 +444,7 @@
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 @@
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 @@
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,37 +516,51 @@
{
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:
+ case AIOGFMT:
{
snd_chan_param *p = (snd_chan_param *)arg;
if (wrch) {
- chn_setformat(wrch, p->play_format);
- chn_setspeed(wrch, p->play_rate);
+ 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_setformat(rdch, p->rec_format);
- chn_setspeed(rdch, p->rec_rate);
+ 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;
}
}
- /* 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;
- }
break;
case AIOGCAP: /* get capabilities */
@@ -554,10 +569,14 @@
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 @@
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 @@
* 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 @@
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 @@
#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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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;
More information about the freebsd-hackers
mailing list