Commit 3d3ae445 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: line6: Fix racy loopback handling

The impulse and monitor handling in submit_audio_out_urb() isn't
protected thus this can be racy with the capture stream handling.
This patch extends the range to protect via each stream's spinlock
(now the whole submit_audio_*_urb() are covered), and take the capture
stream lock additionally for the impulse and monitor handling part.
Tested-by: default avatarChris Rorvick <chris@rorvick.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent d6ca69d8
...@@ -20,21 +20,19 @@ ...@@ -20,21 +20,19 @@
/* /*
Find a free URB and submit it. Find a free URB and submit it.
must be called in line6pcm->in.lock context
*/ */
static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
{ {
int index; int index;
unsigned long flags;
int i, urb_size; int i, urb_size;
int ret; int ret;
struct urb *urb_in; struct urb *urb_in;
spin_lock_irqsave(&line6pcm->in.lock, flags);
index = index =
find_first_zero_bit(&line6pcm->in.active_urbs, LINE6_ISO_BUFFERS); find_first_zero_bit(&line6pcm->in.active_urbs, LINE6_ISO_BUFFERS);
if (index < 0 || index >= LINE6_ISO_BUFFERS) { if (index < 0 || index >= LINE6_ISO_BUFFERS) {
spin_unlock_irqrestore(&line6pcm->in.lock, flags);
dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
return -EINVAL; return -EINVAL;
} }
...@@ -64,7 +62,6 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) ...@@ -64,7 +62,6 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
dev_err(line6pcm->line6->ifcdev, dev_err(line6pcm->line6->ifcdev,
"URB in #%d submission failed (%d)\n", index, ret); "URB in #%d submission failed (%d)\n", index, ret);
spin_unlock_irqrestore(&line6pcm->in.lock, flags);
return 0; return 0;
} }
...@@ -73,15 +70,18 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) ...@@ -73,15 +70,18 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
*/ */
int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm) int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
{ {
int ret, i; unsigned long flags;
int ret = 0, i;
spin_lock_irqsave(&line6pcm->in.lock, flags);
for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
ret = submit_audio_in_urb(line6pcm); ret = submit_audio_in_urb(line6pcm);
if (ret < 0) if (ret < 0)
return ret; break;
} }
return 0; spin_unlock_irqrestore(&line6pcm->in.lock, flags);
return ret;
} }
/* /*
...@@ -137,7 +137,9 @@ void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length) ...@@ -137,7 +137,9 @@ void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
line6pcm->in.bytes += length; line6pcm->in.bytes += length;
if (line6pcm->in.bytes >= line6pcm->in.period) { if (line6pcm->in.bytes >= line6pcm->in.period) {
line6pcm->in.bytes %= line6pcm->in.period; line6pcm->in.bytes %= line6pcm->in.period;
spin_unlock(&line6pcm->in.lock);
snd_pcm_period_elapsed(substream); snd_pcm_period_elapsed(substream);
spin_lock(&line6pcm->in.lock);
} }
} }
...@@ -196,8 +198,6 @@ static void audio_in_callback(struct urb *urb) ...@@ -196,8 +198,6 @@ static void audio_in_callback(struct urb *urb)
if (test_and_clear_bit(index, &line6pcm->in.unlink_urbs)) if (test_and_clear_bit(index, &line6pcm->in.unlink_urbs))
shutdown = 1; shutdown = 1;
spin_unlock_irqrestore(&line6pcm->in.lock, flags);
if (!shutdown) { if (!shutdown) {
submit_audio_in_urb(line6pcm); submit_audio_in_urb(line6pcm);
...@@ -206,6 +206,8 @@ static void audio_in_callback(struct urb *urb) ...@@ -206,6 +206,8 @@ static void audio_in_callback(struct urb *urb)
&line6pcm->flags)) &line6pcm->flags))
line6_capture_check_period(line6pcm, length); line6_capture_check_period(line6pcm, length);
} }
spin_unlock_irqrestore(&line6pcm->in.lock, flags);
} }
/* open capture callback */ /* open capture callback */
......
...@@ -134,11 +134,11 @@ static void add_monitor_signal(struct urb *urb_out, unsigned char *signal, ...@@ -134,11 +134,11 @@ static void add_monitor_signal(struct urb *urb_out, unsigned char *signal,
/* /*
Find a free URB, prepare audio data, and submit URB. Find a free URB, prepare audio data, and submit URB.
must be called in line6pcm->out.lock context
*/ */
static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
{ {
int index; int index;
unsigned long flags;
int i, urb_size, urb_frames; int i, urb_size, urb_frames;
int ret; int ret;
const int bytes_per_frame = line6pcm->properties->bytes_per_frame; const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
...@@ -149,12 +149,10 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) ...@@ -149,12 +149,10 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
(USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL); (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
struct urb *urb_out; struct urb *urb_out;
spin_lock_irqsave(&line6pcm->out.lock, flags);
index = index =
find_first_zero_bit(&line6pcm->out.active_urbs, LINE6_ISO_BUFFERS); find_first_zero_bit(&line6pcm->out.active_urbs, LINE6_ISO_BUFFERS);
if (index < 0 || index >= LINE6_ISO_BUFFERS) { if (index < 0 || index >= LINE6_ISO_BUFFERS) {
spin_unlock_irqrestore(&line6pcm->out.lock, flags);
dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
return -EINVAL; return -EINVAL;
} }
...@@ -187,7 +185,6 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) ...@@ -187,7 +185,6 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
if (urb_size == 0) { if (urb_size == 0) {
/* can't determine URB size */ /* can't determine URB size */
spin_unlock_irqrestore(&line6pcm->out.lock, flags);
dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n"); dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n");
return -EINVAL; return -EINVAL;
} }
...@@ -242,7 +239,8 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) ...@@ -242,7 +239,8 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
urb_out->transfer_buffer_length); urb_out->transfer_buffer_length);
} }
if (line6pcm->prev_fbuf != NULL) { spin_lock_nested(&line6pcm->in.lock, SINGLE_DEPTH_NESTING);
if (line6pcm->prev_fbuf) {
if (line6pcm->flags & LINE6_BITS_PCM_IMPULSE) { if (line6pcm->flags & LINE6_BITS_PCM_IMPULSE) {
create_impulse_test_signal(line6pcm, urb_out, create_impulse_test_signal(line6pcm, urb_out,
bytes_per_frame); bytes_per_frame);
...@@ -266,6 +264,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) ...@@ -266,6 +264,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
bytes_per_frame); bytes_per_frame);
} }
} }
spin_unlock(&line6pcm->in.lock);
ret = usb_submit_urb(urb_out, GFP_ATOMIC); ret = usb_submit_urb(urb_out, GFP_ATOMIC);
...@@ -275,7 +274,6 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) ...@@ -275,7 +274,6 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
dev_err(line6pcm->line6->ifcdev, dev_err(line6pcm->line6->ifcdev,
"URB out #%d submission failed (%d)\n", index, ret); "URB out #%d submission failed (%d)\n", index, ret);
spin_unlock_irqrestore(&line6pcm->out.lock, flags);
return 0; return 0;
} }
...@@ -284,15 +282,18 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) ...@@ -284,15 +282,18 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
*/ */
int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm) int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
{ {
int ret, i; unsigned long flags;
int ret = 0, i;
spin_lock_irqsave(&line6pcm->out.lock, flags);
for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
ret = submit_audio_out_urb(line6pcm); ret = submit_audio_out_urb(line6pcm);
if (ret < 0) if (ret < 0)
return ret; break;
} }
return 0; spin_unlock_irqrestore(&line6pcm->out.lock, flags);
return ret;
} }
/* /*
...@@ -346,8 +347,6 @@ static void audio_out_callback(struct urb *urb) ...@@ -346,8 +347,6 @@ static void audio_out_callback(struct urb *urb)
if (test_and_clear_bit(index, &line6pcm->out.unlink_urbs)) if (test_and_clear_bit(index, &line6pcm->out.unlink_urbs))
shutdown = 1; shutdown = 1;
spin_unlock_irqrestore(&line6pcm->out.lock, flags);
if (!shutdown) { if (!shutdown) {
submit_audio_out_urb(line6pcm); submit_audio_out_urb(line6pcm);
...@@ -356,10 +355,13 @@ static void audio_out_callback(struct urb *urb) ...@@ -356,10 +355,13 @@ static void audio_out_callback(struct urb *urb)
line6pcm->out.bytes += length; line6pcm->out.bytes += length;
if (line6pcm->out.bytes >= line6pcm->out.period) { if (line6pcm->out.bytes >= line6pcm->out.period) {
line6pcm->out.bytes %= line6pcm->out.period; line6pcm->out.bytes %= line6pcm->out.period;
spin_unlock(&line6pcm->out.lock);
snd_pcm_period_elapsed(substream); snd_pcm_period_elapsed(substream);
spin_lock(&line6pcm->out.lock);
} }
} }
} }
spin_unlock_irqrestore(&line6pcm->out.lock, flags);
} }
/* open playback callback */ /* open playback callback */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment