Bug 31429

Summary: kernel resource deadlock on /dev/dsp
Product: Base System Reporter: douzzer <douzzer>
Component: kernAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: Unspecified   
Hardware: Any   
OS: Any   

Description douzzer 2001-10-22 10:10:00 UTC
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.
Comment 1 douzzer 2001-10-22 10:47:26 UTC
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) {
[...]
Comment 2 douzzer 2001-11-05 02:20:02 UTC
I checked the -current tree and discovered that this defect has
already been repaired.  Therefore, the state on this bug can go to
Closed.
Comment 3 dwmalone freebsd_committer freebsd_triage 2001-11-05 09:21:21 UTC
State Changed
From-To: open->closed

Closed at submitters request (already fixed in -current).