Skip to content

Commit 898dfe4

Browse files
committed
ALSA: aloop: Fix racy hw constraints adjustment
The aloop driver tries to update the hw constraints of the connected target on the cable of the opened PCM substream. This is done by adding the extra hw constraints rules referring to the substream runtime->hw fields, while the other substream may update the runtime hw of another side on the fly. This is, however, racy and may result in the inconsistent values when both PCM streams perform the prepare concurrently. One of the reason is that it overwrites the other's runtime->hw field; which is not only racy but also broken when it's called before the open of another side finishes. And, since the reference to runtime->hw isn't protected, the concurrent write may give the partial value update and become inconsistent. This patch is an attempt to fix and clean up: - The prepare doesn't change the runtime->hw of other side any longer, but only update the cable->hw that is referred commonly. - The extra rules refer to the loopback_pcm object instead of the runtime->hw. The actual hw is deduced from cable->hw. - The extra rules take the cable_lock to protect against the race. Fixes: b1c73fc ("ALSA: snd-aloop: Fix hw_params restrictions and checking") Cc: <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent b088b53 commit 898dfe4

File tree

1 file changed

+21
-30
lines changed

1 file changed

+21
-30
lines changed

sound/drivers/aloop.c

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -306,19 +306,6 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
306306
return 0;
307307
}
308308

309-
static void params_change_substream(struct loopback_pcm *dpcm,
310-
struct snd_pcm_runtime *runtime)
311-
{
312-
struct snd_pcm_runtime *dst_runtime;
313-
314-
if (dpcm == NULL || dpcm->substream == NULL)
315-
return;
316-
dst_runtime = dpcm->substream->runtime;
317-
if (dst_runtime == NULL)
318-
return;
319-
dst_runtime->hw = dpcm->cable->hw;
320-
}
321-
322309
static void params_change(struct snd_pcm_substream *substream)
323310
{
324311
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -330,10 +317,6 @@ static void params_change(struct snd_pcm_substream *substream)
330317
cable->hw.rate_max = runtime->rate;
331318
cable->hw.channels_min = runtime->channels;
332319
cable->hw.channels_max = runtime->channels;
333-
params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
334-
runtime);
335-
params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE],
336-
runtime);
337320
}
338321

339322
static int loopback_prepare(struct snd_pcm_substream *substream)
@@ -621,24 +604,29 @@ static unsigned int get_cable_index(struct snd_pcm_substream *substream)
621604
static int rule_format(struct snd_pcm_hw_params *params,
622605
struct snd_pcm_hw_rule *rule)
623606
{
624-
625-
struct snd_pcm_hardware *hw = rule->private;
607+
struct loopback_pcm *dpcm = rule->private;
608+
struct loopback_cable *cable = dpcm->cable;
626609
struct snd_mask m;
627610

628611
snd_mask_none(&m);
629-
m.bits[0] = (u_int32_t)hw->formats;
630-
m.bits[1] = (u_int32_t)(hw->formats >> 32);
612+
mutex_lock(&dpcm->loopback->cable_lock);
613+
m.bits[0] = (u_int32_t)cable->hw.formats;
614+
m.bits[1] = (u_int32_t)(cable->hw.formats >> 32);
615+
mutex_unlock(&dpcm->loopback->cable_lock);
631616
return snd_mask_refine(hw_param_mask(params, rule->var), &m);
632617
}
633618

634619
static int rule_rate(struct snd_pcm_hw_params *params,
635620
struct snd_pcm_hw_rule *rule)
636621
{
637-
struct snd_pcm_hardware *hw = rule->private;
622+
struct loopback_pcm *dpcm = rule->private;
623+
struct loopback_cable *cable = dpcm->cable;
638624
struct snd_interval t;
639625

640-
t.min = hw->rate_min;
641-
t.max = hw->rate_max;
626+
mutex_lock(&dpcm->loopback->cable_lock);
627+
t.min = cable->hw.rate_min;
628+
t.max = cable->hw.rate_max;
629+
mutex_unlock(&dpcm->loopback->cable_lock);
642630
t.openmin = t.openmax = 0;
643631
t.integer = 0;
644632
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
@@ -647,11 +635,14 @@ static int rule_rate(struct snd_pcm_hw_params *params,
647635
static int rule_channels(struct snd_pcm_hw_params *params,
648636
struct snd_pcm_hw_rule *rule)
649637
{
650-
struct snd_pcm_hardware *hw = rule->private;
638+
struct loopback_pcm *dpcm = rule->private;
639+
struct loopback_cable *cable = dpcm->cable;
651640
struct snd_interval t;
652641

653-
t.min = hw->channels_min;
654-
t.max = hw->channels_max;
642+
mutex_lock(&dpcm->loopback->cable_lock);
643+
t.min = cable->hw.channels_min;
644+
t.max = cable->hw.channels_max;
645+
mutex_unlock(&dpcm->loopback->cable_lock);
655646
t.openmin = t.openmax = 0;
656647
t.integer = 0;
657648
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
@@ -716,19 +707,19 @@ static int loopback_open(struct snd_pcm_substream *substream)
716707
/* are cached -> they do not reflect the actual state */
717708
err = snd_pcm_hw_rule_add(runtime, 0,
718709
SNDRV_PCM_HW_PARAM_FORMAT,
719-
rule_format, &runtime->hw,
710+
rule_format, dpcm,
720711
SNDRV_PCM_HW_PARAM_FORMAT, -1);
721712
if (err < 0)
722713
goto unlock;
723714
err = snd_pcm_hw_rule_add(runtime, 0,
724715
SNDRV_PCM_HW_PARAM_RATE,
725-
rule_rate, &runtime->hw,
716+
rule_rate, dpcm,
726717
SNDRV_PCM_HW_PARAM_RATE, -1);
727718
if (err < 0)
728719
goto unlock;
729720
err = snd_pcm_hw_rule_add(runtime, 0,
730721
SNDRV_PCM_HW_PARAM_CHANNELS,
731-
rule_channels, &runtime->hw,
722+
rule_channels, dpcm,
732723
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
733724
if (err < 0)
734725
goto unlock;

0 commit comments

Comments
 (0)