Hello Clemens and Takashi (and list),
I've been living with USB audio dropping partial frames on UHCI controllers for quite a few years. I've finally found the cause (DMA starvation) and have a lightweight fix.
problem summary:
Playback (but not capture) on UHCI controllers randomly loses samples; they are skipped completely during playback. This happens anywhere from a few times a second to once an hour or so. These are not ALSA underruns; the UHCI host controller itself is reporting hard DMA starvation errors. ALSA ignores these error and continues as if nothing happened, and the UHCI linux driver does not log this error anywhere.
UHCI overlaps sending on the USB bus with DMAing the data from memory. It does not have all the data it needs to transmit a complete packet before it begins doing so. If the data it needs is in a dirty cache line on a CPU, the flush triggers after DMA is already started; the UHCI host controller has to wait for the flush before accessing it, and if the flush happens too late, UHCI aborts the packet. This is happening regularly on all the UHCI controllers I've tried thusfar.
My guess is that this issue never attracted much attention because a) every one knows isoch transfers are 'unreliable' and b) other transfer types automatically retry so they eventually succeed without any obvious sign that something went wrong (and they're usually smaller than one cacheline and so unaffected anyway).
Fix:
The fix for usbaudio is dead easy. Call:
clflush_cache_range(urb->transfer_buffer, urb->transfer_buffer_length);
as the last step of prepare_playback_urb() and prepare_nodata_playback_urb(). This completely corrects the DMA starvation behavior on my machines here.
Note that the clflush_cache_range() has nothing to do with maintaining coherency. The call is only to force the flush ahead of time to avoid the fatal latency spikes from on-demand flushes when DMA is already running.
The drawback to this fix is that it's in one high level driver (ALSA USB audio) instead of inside UHCI where it really belongs. Unfortunately, the URB abstraction gives us no reliable way to do it in the HCD, because other drivers don't necessarily pass a valid CPU-side buffer address when using URB_NO_TRANSFER_DMA_MAP.
Should I whip up some patches for usb audio for 2.6.x and 3.3.x and leave it at that, or should we discuss a bit more 'why isn't the kernel doing this for us?'
Monty