[alsa-devel] multi pcm and mmap problem

Takashi Iwai tiwai at suse.de
Fri Nov 23 14:41:43 CET 2007


At Wed, 21 Nov 2007 16:27:52 -0500,
Tim wrote:
> 
> On Wednesday 21 November 2007 06:01:22 am you wrote:
> > At Wed, 21 Nov 2007 01:58:41 -0500,
> >
> > terminator356 at users.sourceforge.net wrote:
> > > After 2 years I've finally traced and fixed a problem
> > >  which was preventing me from using anything after
> > >  ALSA 1.0.9b !
> > >
> > > In ALSA lib 1.0.9b:
> > > pcm/pcm_generic.c:
> > > int snd_pcm_generic_channel_info()
> > > {
> > >         snd_pcm_generic_t *generic = pcm->private_data;
> > >                 return snd_pcm_channel_info(generic->slave, info);
> > > }
> > >
> > > But in ALSA lib 1.0.15:
> > > pcm/pcm_generic.c:
> > > int snd_pcm_generic_channel_info()
> > > {
> > >         snd_pcm_generic_t *generic = pcm->private_data;
> > >         if (pcm->mmap_shadow) {
> > >                 /* No own buffer is required - the plugin won't change
> > >                  * the data on the buffer, or do safely on-the-place
> > >                  * conversion
> > >                  */
> > >                 return snd_pcm_channel_info(generic->slave, info);
> > >         } else {
> > >                // Tim: TESTED: This is now called instead of the other
> > > one! /* Allocate own buffer */
> > >                 return snd_pcm_channel_info_shm(pcm, info, -1);
> > >         }
> > > }
> > >
> > > My multi PCM consists of a plughw:0,2 and a hw:1,0
> > > In ALSA 1.0.9b, all five PCMs ALSA creates for this arrangement
> > >  create their areas with mmap(). And it works.
> > > But in ALSA 1.0.15 my hw:1,0 and its multi PCM areas are
> > >  created with mmap(), and my plughw:0,2 and its PCMs areas
> > >  are created with malloc().
> > > Can't hear/record any sound from the plughw:0,2 part of the multi.
> > >
> > > I have verified that ALSA 1.0.15 is copying the plughw:0,2 HW PCM
> > >  areas to its LINEAR PCM areas, but NOT finally to its multi PCM areas.
> > > It just copies all zeros.
> > > That's the reason I hear/record no sound from the plughw:0,2 part
> > >  of the multi PCM.
> > > So I reverted the function's code back to 1.0.9b and presto!
> > > It works now, the same as in ALSA 1.0.9b
> > >
> > > So please tell me, am I missing something?
> >
> > Well, first of all, could you elaborate what is the real problem you
> > are facing?  Judging from your description above, it sounds like a
> > clear bug, but I'm not sure where to start.
> >
> > In addition to the detailed bug description, a testcase program would
> > be greatly helpful.
> >
> 
> OK. First, here's my simple testing .asoundrc :
> pcm.multi_rec {
>             type multi;
>             slaves.a.pcm "hw:1,0";
>             slaves.a.channels 12;
>             slaves.b.pcm "plughw:0,2";
>             slaves.b.channels 16;
>             bindings.0.slave a;
>             bindings.0.channel 4;
>             bindings.1.slave b;
>             bindings.1.channel 10;
>           }
> ctl.multi_rec {
>             type hw;
>             card 0;
>           }
> 
> The two cards are SBLive! (emu10k1) and Delta1010LT (ice1712).
> 
> And here is the command line I run:
>    arecord -I -v -t wav -c2 -r 48000 -f S32_LE -Dmulti_rec -d 3   \
>       --buffer-size 128 arecord-multi-ice-emu.wav
> 
> My problem is under ALSA 1.0.15 no sound is recorded from slave 'b' - 
>  (the plughw:0,2 channel).
> When I look at the two generated wav files, the first has data, and the second
>  has all zeros.
> 
> So as I said before, I found that ALSA is copying data from the 
>  plughw:0,2 HW PCM area to its LINEAR PCM area (i.e. the actual conversion
>  is taking place), but then somehow the data is not finally copied to its
>  multi slave b PCM area - just all zeros are copied.
> Hence all zeros are stored in the second wav file.
> 
> But with ALSA 1.0.9b, since all the areas are allocated with mmap(),
>  it works fine. It is only after ALSA 1.0.9b, that I get the problem.
> Because ALSA 1.0.15 is allocating both the plughw:0,2 LINEAR PCM area 
>  and multi slave slave b's PCM area with malloc().
> It seems ALSA doesn't want to copy to areas created with malloc().

OK, point taken.

The problem is that the multi plugin creates unnecessary local buffer.
It works somehow when the slave is a hw, but it doesn't for other
types.

Try the patch below.  This should fix the problem.  Now the multi
plugin just shadows the slave buffers via mmap.


> > > Should I add something more to my .asoundrc ?
> > > I see that besides {type multi ... }, there is a {type share ...}
> > > defined. *** Does the 'share' have any use here? Can't find any docs on
> > > it.
> >
> > The share type is used only for share plugin, so you can ignore it.
> >
> > > Also:
> > > Yes, I can see now, why you made this code change.
> > > mmap() has limits, so you switched over to malloc().
> > > Those limits force the user to use small buffers
> > >  and/or reduce the number of channels created.
> >
> > No, mmap was never used between plugins.  It was only for the hw layer
> > even in the earlier verson.  In the earlier version, SHM was used
> > instead.  But, the plugin buffer is referred only locally, so it makes
> > no sense to use SHM for that.  That's why now it was switched to
> > malloc.  (snd_pcm_channel_info_shm() is a rest of the old behavior,
> > and thus its name is confusing...)
> >
> My point was this: 
> Under ALSA 1.0.9b, (and with my fix to ALSA 1.0.15 to force it to use
>  mmap() always - see above), if I run:
>      arecord -I -v -t wav -c2 -r 48000 -f S32_LE -Dmulti_rec -d 3   \
>         --buffer-size 128 arecord-multi-ice-emu.wav
>  it will run. But if I run
>      arecord -I -v -t wav -c2 -r 48000 -f S32_LE -Dmulti_rec -d 3   \
>         --buffer-size 256 arecord-multi-ice-emu.wav
>  it fails with "mmap() failed: Invalid argument".
> I quadrupled the available locked memory with a line in 
>     /etc/security/limits.conf 
>     *               -       memlock          128 
>  but the second command line still fails. 
> What am I doing wrong? How can I increase mmap() limits?

It must be irrelevant now...


Takashi

diff -r 3539f279ec38 src/pcm/pcm_multi.c
--- a/src/pcm/pcm_multi.c	Wed Nov 21 12:19:43 2007 +0100
+++ b/src/pcm/pcm_multi.c	Fri Nov 23 14:40:39 2007 +0100
@@ -690,13 +690,43 @@ static snd_pcm_sframes_t snd_pcm_multi_m
 	return size;
 }
 
-static int snd_pcm_multi_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+static int snd_pcm_multi_munmap(snd_pcm_t *pcm)
 {
+	free(pcm->mmap_channels);
+	free(pcm->running_areas);
+	pcm->mmap_channels = NULL;
+	pcm->running_areas = NULL;
 	return 0;
 }
 
-static int snd_pcm_multi_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+static int snd_pcm_multi_mmap(snd_pcm_t *pcm)
 {
+	snd_pcm_multi_t *multi = pcm->private_data;
+	unsigned int c;
+
+	pcm->mmap_channels = calloc(pcm->channels,
+				    sizeof(pcm->mmap_channels[0]));
+	pcm->running_areas = calloc(pcm->channels,
+				    sizeof(pcm->running_areas[0]));
+	if (!pcm->mmap_channels || !pcm->running_areas) {
+		snd_pcm_multi_munmap(pcm);
+		return -ENOMEM;
+	}
+
+	/* Copy the slave mmapped buffer data */
+	for (c = 0; c < pcm->channels; c++) {
+		snd_pcm_multi_channel_t *chan = &multi->channels[c];
+		snd_pcm_t *slave;
+		if (chan->slave_idx < 0) {
+			snd_pcm_multi_munmap(pcm);
+			return -ENXIO;
+		}
+		slave = multi->slaves[chan->slave_idx].pcm;
+		pcm->mmap_channels[c] =
+			slave->mmap_channels[chan->slave_channel];
+		pcm->running_areas[c] =
+			slave->running_areas[chan->slave_channel];
+	}
 	return 0;
 }
 
@@ -850,6 +880,7 @@ int snd_pcm_multi_open(snd_pcm_t **pcmp,
 		return err;
 	}
 	pcm->mmap_rw = 1;
+	pcm->mmap_shadow = 1; /* has own mmap method */
 	pcm->ops = &snd_pcm_multi_ops;
 	pcm->fast_ops = &snd_pcm_multi_fast_ops;
 	pcm->private_data = multi;



More information about the Alsa-devel mailing list