| Summary: | kernel resource deadlock on /dev/dsp | ||
|---|---|---|---|
| Product: | Base System | Reporter: | douzzer <douzzer> |
| Component: | kern | Assignee: | freebsd-bugs (Nobody) <bugs> |
| Status: | Closed FIXED | ||
| Severity: | Affects Only Me | ||
| Priority: | Normal | ||
| Version: | Unspecified | ||
| Hardware: | Any | ||
| OS: | Any | ||
My original submission of half an hour ago was rather flawed, in two
respects:
(1) apparently, it doesn't matter if the dsp was opened WRONLY or RDWR
- a subsequent RDONLY open results in EBUSY either way.
(2) my fix was profoundly bogus, in particular causing a kernel hang
on reboot (probably due to some other resource deadlock introduced
by the code).
Here is a much cleaner fix that seems to actually work all-around.
The changes are that d->arec[chan] is updated immediately after the
allocation, before the pointer can get lost, and the last EBUSY return
in the FWRITE section includes the same un-busying code as the one
before it. I'm not sufficiently familiar with the subsystem to know
if it's important to update d->arec[chan] scrupulously (as I have it
doing now), but it doesn't hurt anything apparently.
int
dsp_open(snddev_info *d, int chan, int oflags, int devtype)
{
pcm_channel *rdch, *wrch;
u_int32_t fmt;
if (chan >= d->chancount) return ENODEV;
if ((d->flags & SD_F_SIMPLEX) && (d->ref[chan] > 0))
return EBUSY;
if (d->atype[chan] != 0 && d->atype[chan] != devtype)
return EBUSY;
rdch = d->arec[chan];
wrch = d->aplay[chan];
if (oflags & FREAD) {
if (rdch == NULL) {
rdch = allocchn(d, PCMDIR_REC);
if (!rdch)
return EBUSY;
d->arec[chan] = rdch;
} else
return EBUSY;
}
if (oflags & FWRITE) {
if (wrch == NULL) {
wrch = allocchn(d, PCMDIR_PLAY);
if (!wrch) {
if (rdch && (oflags & FREAD))
rdch->flags &= ~CHN_F_BUSY;
return EBUSY;
}
} else {
if (rdch && (oflags & FREAD))
rdch->flags &= ~CHN_F_BUSY;
return EBUSY;
}
}
d->atype[chan] = devtype;
d->aplay[chan] = wrch;
d->ref[chan]++;
switch (devtype) {
[...]
I checked the -current tree and discovered that this defect has already been repaired. Therefore, the state on this bug can go to Closed. State Changed From-To: open->closed Closed at submitters request (already fixed in -current). |
dsp_open() in sys/dev/sound/pcm/dsp.c fails to release or remember the read channel (rdch) if the subsequent wrch allocation craps out. This causes all subsequent attempts to allocate the rdch to fail, because it is already allocated (but the pointer was discarded). Fix: I'm sure you can do a cleaner job, but I just wanted to get this fixed prontissimo: int dsp_open(snddev_info *d, int chan, int oflags, int devtype) { pcm_channel *rdch, *wrch; u_int32_t fmt; int gotrdchthiscall = 0; if (chan >= d->chancount) return ENODEV; if ((d->flags & SD_F_SIMPLEX) && (d->ref[chan] > 0)) return EBUSY; if (d->atype[chan] != 0 && d->atype[chan] != devtype) return EBUSY; rdch = d->arec[chan]; wrch = d->aplay[chan]; if (oflags & FREAD) { if (rdch == NULL) { rdch = allocchn(d, PCMDIR_REC); if (!rdch) return EBUSY; gotrdchthiscall = 1; } else return EBUSY; } if (oflags & FWRITE) { if (wrch == NULL) { wrch = allocchn(d, PCMDIR_PLAY); if (!wrch) { if (rdch && (oflags & FREAD)) rdch->flags &= ~CHN_F_BUSY; goto busyret; } } else goto busyret; goto nobusyret; busyret: /* must liberate the rdch if it we opened it this call */ if (gotrdchthiscall) { chn_abort(rdch); rdch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); chn_reset(rdch, 0); } return EBUSY; nobusyret: } [...] How-To-Repeat: start sound playing with xmms or any other program that opens the dsp read-write. try to record sound (from the same dsp of course) - EBUSY results. stop sound playing. try record again. should work, but instead, more EBUSYs result.