Query about xrun on usb/pcm

Takashi Iwai tiwai at suse.de
Tue Nov 22 15:22:50 CET 2022


On Tue, 22 Nov 2022 15:19:13 +0100,
Takashi Iwai wrote:
> 
> On Tue, 22 Nov 2022 12:16:47 +0100,
> Carl Hetherington wrote:
> > 
> > Hi Takashi,
> > 
> > Thank you for getting back to me!
> > 
> > On Tue, 22 Nov 2022, Takashi Iwai wrote:
> > 
> > [snip]
> > 
> > > > Now, snd_usb_endpoint_start() is called on 0x2 and that is fine.  Next,
> > > > snd_usb_endpoint_start() is called on 0x82 and that fails because its
> > > > state is still STOPPING.
> > > >
> > > > At this point things seem broken.
> > > >
> > > > Does anyone have a hint about where in this sequence things are going
> > > > wrong, and maybe even why?
> > >
> > > The problem is that it's treating XRUNs on the both streams
> > > individually.  It's correct to recover only the PCM stream when an
> > > XRUN is reported to the PCM stream.  However, for an XRUN on the
> > > capture stream that serves as a sync source, it should stop and
> > > recover not only the capture PCM stream but also the playback stream
> > > as a sync sink as well.
> > >
> > > Below is a possible test fix (totally untested!).
> > > This may give XRUNs twice eventually, which is a bit confusing, but it
> > > aligns with the actual hardware behavior, at least.
> > 
> > [snip fix]
> > 
> > Makes sense, thank you!  Sadly, the fix doesn't seem to work because (I
> > think) the xruns I'm seeing come via a different path (not though
> > notify_xrun()).  Mine arrive via this trace:
> > 
> > __snd_pcm_xrun
> > snd_pcm_update_state
> > snd_pcm_update_hw_ptr
> > usb_hcd_giveback_urb
> > snd_pcm_period_elapsed_under_stream_lock
> > snd_pcm_period_elapsed
> > retire_capture_urb
> > snd_complete_urb
> > 
> > I'll see if can apply a similar fix to this case, though to my naive
> > eyes it looks a little trickier as the xrun is found in the snd_pcm
> > code rather than the USB code.  Any suggestions most welcome!
> 
> OK, then it's a bit different problem, and not so trivial to fix in
> the kernel side alone, I'm afraid.  Basically it's a race between
> start and stop of two streams.  The key point is that, for stopping a
> (USB) stream, a sync-stop operation is needed, and this can't be
> performed at the PCM trigger itself (which is an atomic operation).
> So, the kernel trigger may at most return an error there.
> 
> I assume that it's from snd_usb_endpoint_start() and it returning
> -EPIPE error.  If so, we may change the PCM core code to set the PCM
> state again XRUN in such an error case, so that application may repeat
> the standard recovery process.  Something like below.

Also, it might be slightly better if we swap the starting order of two
streams: sync at first, then data.  A race can still happen, though.


Takashi

-- 8< --
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -241,19 +241,19 @@ static int start_endpoints(struct snd_usb_substream *subs)
 	if (!subs->data_endpoint)
 		return -EINVAL;
 
-	if (!test_and_set_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) {
-		err = snd_usb_endpoint_start(subs->data_endpoint);
+	if (subs->sync_endpoint &&
+	    !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
+		err = snd_usb_endpoint_start(subs->sync_endpoint);
 		if (err < 0) {
-			clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags);
+			clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
 			goto error;
 		}
 	}
 
-	if (subs->sync_endpoint &&
-	    !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
-		err = snd_usb_endpoint_start(subs->sync_endpoint);
+	if (!test_and_set_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) {
+		err = snd_usb_endpoint_start(subs->data_endpoint);
 		if (err < 0) {
-			clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
+			clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags);
 			goto error;
 		}
 	}


More information about the Alsa-devel mailing list