Skip to content

Commit 8c721c5

Browse files
committed
ALSA: usb-audio: Fix recursive locking at XRUN during syncing
The recent support of low latency playback in USB-audio driver made the snd_usb_queue_pending_output_urbs() function to be called via PCM ack ops. In the new code path, the function is performed already in the PCM stream lock. The problem is that, when an XRUN is detected, the function calls snd_pcm_xrun() to notify, but snd_pcm_xrun() is supposed to be called only outside the stream lock. As a result, it leads to a deadlock of PCM stream locking. For avoiding such a recursive locking, this patch adds an additional check to the code paths in PCM core that call the ack callback; now it checks the error code from the callback, and if it's -EPIPE, the XRUN is handled in the PCM core side gracefully. Along with it, the USB-audio driver code is changed to follow that, i.e. -EPIPE is returned instead of the explicit snd_pcm_xrun() call when the function is performed already in the stream lock. Fixes: d5f871f ("ALSA: usb-audio: Improved lowlatency playback support") Reported-and-tested-by: John Keeping <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Jaroslav Kysela <[email protected]> Reviewed-by; Takashi Sakamoto <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent b871cb9 commit 8c721c5

File tree

4 files changed

+19
-11
lines changed

4 files changed

+19
-11
lines changed

sound/core/pcm_lib.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,6 +2155,8 @@ int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream,
21552155
ret = substream->ops->ack(substream);
21562156
if (ret < 0) {
21572157
runtime->control->appl_ptr = old_appl_ptr;
2158+
if (ret == -EPIPE)
2159+
__snd_pcm_xrun(substream);
21582160
return ret;
21592161
}
21602162
}

sound/usb/endpoint.c

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -455,8 +455,8 @@ static void push_back_to_ready_list(struct snd_usb_endpoint *ep,
455455
* This function is used both for implicit feedback endpoints and in low-
456456
* latency playback mode.
457457
*/
458-
void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
459-
bool in_stream_lock)
458+
int snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
459+
bool in_stream_lock)
460460
{
461461
bool implicit_fb = snd_usb_endpoint_implicit_feedback_sink(ep);
462462

@@ -480,7 +480,7 @@ void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
480480
spin_unlock_irqrestore(&ep->lock, flags);
481481

482482
if (ctx == NULL)
483-
return;
483+
break;
484484

485485
/* copy over the length information */
486486
if (implicit_fb) {
@@ -495,25 +495,31 @@ void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
495495
break;
496496
if (err < 0) {
497497
/* push back to ready list again for -EAGAIN */
498-
if (err == -EAGAIN)
498+
if (err == -EAGAIN) {
499499
push_back_to_ready_list(ep, ctx);
500-
else
500+
break;
501+
}
502+
503+
if (!in_stream_lock)
501504
notify_xrun(ep);
502-
return;
505+
return -EPIPE;
503506
}
504507

505508
err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
506509
if (err < 0) {
507510
usb_audio_err(ep->chip,
508511
"Unable to submit urb #%d: %d at %s\n",
509512
ctx->index, err, __func__);
510-
notify_xrun(ep);
511-
return;
513+
if (!in_stream_lock)
514+
notify_xrun(ep);
515+
return -EPIPE;
512516
}
513517

514518
set_bit(ctx->index, &ep->active_mask);
515519
atomic_inc(&ep->submitted_urbs);
516520
}
521+
522+
return 0;
517523
}
518524

519525
/*

sound/usb/endpoint.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
5252
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
5353
struct snd_urb_ctx *ctx, int idx,
5454
unsigned int avail);
55-
void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
56-
bool in_stream_lock);
55+
int snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
56+
bool in_stream_lock);
5757

5858
#endif /* __USBAUDIO_ENDPOINT_H */

sound/usb/pcm.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1639,7 +1639,7 @@ static int snd_usb_pcm_playback_ack(struct snd_pcm_substream *substream)
16391639
* outputs here
16401640
*/
16411641
if (!ep->active_mask)
1642-
snd_usb_queue_pending_output_urbs(ep, true);
1642+
return snd_usb_queue_pending_output_urbs(ep, true);
16431643
return 0;
16441644
}
16451645

0 commit comments

Comments
 (0)