[alsa-devel] Bug in alsa-lib causes high CPU with rate plugin

Trent Piepho tpiepho at gmail.com
Sat Nov 10 02:19:32 CET 2012

On Fri, Nov 9, 2012 at 8:57 AM, Takashi Iwai <tiwai at suse.de> wrote:
> At Thu, 8 Nov 2012 13:47:52 -0800,
> Trent Piepho wrote:
>> snd_pcm_wait() doesn't sleep, because it's based on the status of the
>> hw buffer and that buffer does have free space.  snd_pcm_write_areas()
>> would write to the hw buffer and not try to sleep.  But it's not
>> looking at the hw buffer when it decides to sleep or write, it's
>> looking at the rate buffer.  And the rate buffer's fill level says to
>> sleep, not write.  And if snd_pcm_wait() was looking at the rate
>> buffer, it would sleep.
> Yeah, this is a known longtime PITA in the rate plugin.
> Tweaking avail_min is also an option, but I'm afraid it's too
> hackish.
> The primary problem is the discrepancy of the state "I have to wait
> until period size becomes available" between the rate plugin and the
> underlying PCM.  Since the poll is performed only on the slave PCM,
> checking the rate plugin's avail is bogus in such a state.  In other
> words, if the rate plugin puts the decision for the sleep simply
> depending on the slave state, there will be no inconsistency between
> them.
> Below is a quick fix.  Does it work for you?

It adds an extra call to the kernel to get the HW pointer position yet
again.  snd_pcm_avail_update() is called initially by
snd_pcm_write_areas.  The avail_update method for the rate plugin
calls snd_pcm_avail_update on the slave.  It's worth noting at this
point that we know that avail(rate) + unresampled_data = avail(slave).
  Then check_for_wait() is called right after, which calls the rate
plugin's check_for_wait method, which calls snd_pcm_avail_update() on
the slave a second time.

It seems like you have a race if a period elapses between the two
checks of the slave's avail.  Suppose upon entering
snd_pcm_write_areas both buffers are 100% full.  avail =
snd_pcm_update_avail(pcm /*rate*/) returns zero, which is up to date
and correct.  We get to the code for check_for_wait() and it calls
snd_pcm_avail_update(slave) and a period has elapsed since "avail" was
found. So check_for_wait() returns not to wait, which is correct based
on the current state of the slave.  Now the rest of the
snd_pcm_write_areas() code runs, but avail=0 still and it's not going
to work, it just blows up if avail = 0.

If you think about it, tweaking avail_min does the same thing as using
the slave's avail and avail_min.

We had a wait check of "rate->avail < rate->avail_min".  What we want
is "slave->avail < slave->avail_min".  Ignoring the resampling factor,
we know that:
rate->avail + rate->unresampled_data = slave->avail
rate->avail_min = slave->avail_min

So if we do a little algebra:
slave->avail < slave->avail_min
rate->avail + rate->unresampled_data < slave->avail_min
rate->avail + rate->unresampled_data < rate->avail_min
rate->avail < rate->avail_min - rate->unresampled_data

In other words, subtracting the unresampled data from the avail_min
for the rate pcm produces the exact same comparison as looking at the
slave's avail and avail_min, which is what we actually wanted.

More information about the Alsa-devel mailing list