In the process of trying to discover that kind of locking ALSA provides to a driver and what the driver must take care of itself, I think I have found some race conditions inside the alsa core.
I think there are likely quite a few, but this example was one of the easiest to prove.
ALSA uses unlocked_ioctl, which means ioctl calls aren't protected by the BKL and may run at the same time.
Some ioctls, for instance HW_PARAMS, don't appear to have any locking done by ALSA. It's entirely possible for two hw_params calls to run at the same time on the same substream, and the ALSA core (and probably many drivers too) can't handle this.
The following patch helps to create a larger window for the race condition, so that it can be triggered consistently.
--- a/sound/core/pcm_native.c Tue Sep 04 09:02:06 2007 +0000 +++ b/sound/core/pcm_native.c Sat Sep 22 17:12:37 2007 -0700 @@ -411,6 +411,7 @@ static int snd_pcm_hw_params(struct snd_ runtime->channels = params_channels(params); runtime->rate = params_rate(params); runtime->period_size = params_period_size(params); + { static unsigned long x=1; if(test_and_change_bit(0, &x)) msleep(10); } runtime->periods = params_periods(params); runtime->buffer_size = params_buffer_size(params); runtime->tick_time = params_tick_time(params);
This causes every other call to snd_pcm_hw_params() to have a 10ms delay between setting the period size and period count. If there are two calls within 10ms, the period count should come from the first call (which slept), while the period size will be from the second call.
My test program will call the HW_PARAMS ioctl twice from different threads running at the same time. The first call will ask for the maximum number of periods with minimum size, the second call for the minimum number of periods of maximum size.
When prepare is called, we discover that the runtime is set for the maximum number of periods of maximum size, which exceeds the buffer size. Of course even if it didn't exceed the buffer size, the params should be from one call or the other, not some random combination of both.
I've attaching a very simple dummy ALSA driver which shows this. All it does is print timestamps and a few parameters when the various callback functions are invoked. The output looks like this:
as_open - 0.084 us as_hw_params - 552.864 us periods 128, period size 1024 as_hw_params - 589.996 us periods 1, period size 131072 as_prepare - 11065.307 us periods 128, period size 131072 as_close - 11278.229 us
Notice how in prepare the runtime has parameters that are a combination of the two hw_params calls. This test program should trigger the same race with any other ALSA driver. Just adjust the period count and size.
The test program doesn't use the ALSA lib. It does need the headers from the alsa lib package to compile, since this seems to be the only place where user space headers for the device interface are provided.