On Mon, 29 Jul 2013, Clemens Ladisch wrote: > Alan Stern wrote: > > Clemens remarked some time ago that keeping the queue full would be > > trivial, if only he knew how full it needed to be. The answer to that > > is given above. I have been trying to make the appropriate changes, > > but I'm not finding it "trivial". > > What I meant was that it would be trivial to change the lower bound of > the period size (from which many queueing parameters are derived). Here's what I've got. In turns out the predicting the optimum number of URBs needed is extremely difficult. I decided it would be better to make an overestimate and then to submit URBs as needed, rather than keeping all of them active all the time. I ended up with this patch (untested). It is certainly incomplete because it doesn't address endpoints with implicit sync. It also suffers from a race between snd_submit_urbs() and deactivate_urbs(): an URB may be resubmitted after it has been deactivated. (In all fairness, I think this race probably exists in the current code too -- there are no spinlocks for synchronization.) The patch also fixes a couple of unrelated items: MAX_PACKS vs. MAX_PACKS_HS, and the maxsize calculation should realize that a packet can't contain a partial frame. What do you think of this approach? Alan Stern sound/usb/card.h | 7 + sound/usb/endpoint.c | 185 +++++++++++++++++++++++++++++---------------------- sound/usb/pcm.c | 3 3 files changed, 114 insertions(+), 81 deletions(-) Index: usb-3.11/sound/usb/card.h =================================================================== --- usb-3.11.orig/sound/usb/card.h +++ usb-3.11/sound/usb/card.h @@ -4,7 +4,7 @@ #define MAX_NR_RATES 1024 #define MAX_PACKS 20 #define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */ -#define MAX_URBS 8 +#define MAX_URBS 11 #define SYNC_URBS 4 /* always four urbs for sync */ #define MAX_QUEUE 24 /* try not to exceed this queue length, in ms */ @@ -43,6 +43,7 @@ struct snd_urb_ctx { struct snd_usb_endpoint *ep; int index; /* index for urb array */ int packets; /* number of packets per urb */ + int data_packets; /* current number of data packets */ int packet_size[MAX_PACKS_HS]; /* size of packets for next submission */ struct list_head ready_list; }; @@ -99,6 +100,10 @@ struct snd_usb_endpoint { int skip_packets; /* quirks for devices to ignore the first n packets in a stream */ + unsigned int min_queued_packs; /* USB hardware queue requirement */ + unsigned int queued_packs; /* number of packets currently queued */ + unsigned int queued_urbs; /* number of URBs currently queued */ + int next_urb; /* next to submit */ spinlock_t lock; struct list_head list; }; Index: usb-3.11/sound/usb/pcm.c =================================================================== --- usb-3.11.orig/sound/usb/pcm.c +++ usb-3.11/sound/usb/pcm.c @@ -1328,7 +1328,7 @@ static void prepare_playback_urb(struct stride = runtime->frame_bits >> 3; frames = 0; - urb->number_of_packets = 0; + ctx->data_packets = urb->number_of_packets = 0; spin_lock_irqsave(&subs->lock, flags); for (i = 0; i < ctx->packets; i++) { if (ctx->packet_size[i]) @@ -1341,6 +1341,7 @@ static void prepare_playback_urb(struct urb->iso_frame_desc[i].length = counts * ep->stride; frames += counts; urb->number_of_packets++; + ctx->data_packets++; subs->transfer_done += counts; if (subs->transfer_done >= runtime->period_size) { subs->transfer_done -= runtime->period_size; Index: usb-3.11/sound/usb/endpoint.c =================================================================== --- usb-3.11.orig/sound/usb/endpoint.c +++ usb-3.11/sound/usb/endpoint.c @@ -217,6 +217,7 @@ static void prepare_outbound_urb(struct } urb->number_of_packets = ctx->packets; + ctx->data_packets = ctx->packets; urb->transfer_buffer_length = offs * ep->stride; memset(urb->transfer_buffer, ep->silence_value, offs * ep->stride); @@ -273,6 +274,7 @@ static inline void prepare_inbound_urb(s urb->transfer_buffer_length = offs; urb->number_of_packets = urb_ctx->packets; + urb_ctx->data_packets = urb_ctx->packets; break; case SND_USB_ENDPOINT_TYPE_SYNC: @@ -341,6 +343,47 @@ static void queue_pending_output_urbs(st } } +/** + * snd_submit_urbs: Submit data URBs for endpoints without implicit feedback + * + * @ep: The endpoint to use + * @ctx: Context for the first URB on the queue (or to be queued) + * + * Submit enough URBs so that when the next one completes, the hardware queue + * will still contain sufficiently many packets. + * + * Note: If the hardware queue is empty (and @ctx refers to the next URB to be + * submitted), then ctx->data_packets must be equal to 0. + */ +static int snd_submit_urbs(struct snd_usb_endpoint *ep, struct snd_urb_ctx *ctx) +{ + int err = 0; + + while (ep->queued_packs - ctx->data_packets < ep->min_queued_packs && + ep->queued_urbs < ep->nurbs) { + struct snd_urb_ctx *u = &ep->urb[ep->next_urb]; + + if (usb_pipeout(ep->pipe)) + prepare_outbound_urb(ep, u); + else + prepare_inbound_urb(ep, u); + + err = usb_submit_urb(u->urb, GFP_ATOMIC); + if (err) { + snd_printk(KERN_ERR "cannot submit urb (err = %d: %s)\n", + err, usb_error_string(err)); + break; + } + set_bit(ep->next_urb, &ep->active_mask); + ep->queued_packs += u->data_packets; + ++ep->queued_urbs; + + if (++ep->next_urb >= ep->nurbs) + ep->next_urb = 0; + } + return err; +} + /* * complete callback for urbs */ @@ -348,20 +391,23 @@ static void snd_complete_urb(struct urb { struct snd_urb_ctx *ctx = urb->context; struct snd_usb_endpoint *ep = ctx->ep; - int err; + + clear_bit(ctx->index, &ep->active_mask); + ep->queued_packs -= ctx->data_packets; + --ep->queued_urbs; if (unlikely(urb->status == -ENOENT || /* unlinked */ urb->status == -ENODEV || /* device removed */ urb->status == -ECONNRESET || /* unlinked */ urb->status == -ESHUTDOWN || /* device disabled */ ep->chip->shutdown)) /* device disconnected */ - goto exit_clear; + return; if (usb_pipeout(ep->pipe)) { retire_outbound_urb(ep, ctx); /* can be stopped during retire callback */ if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags))) - goto exit_clear; + return; if (snd_usb_endpoint_implicit_feedback_sink(ep)) { unsigned long flags; @@ -371,28 +417,24 @@ static void snd_complete_urb(struct urb spin_unlock_irqrestore(&ep->lock, flags); queue_pending_output_urbs(ep); - goto exit_clear; + return; } - prepare_outbound_urb(ep, ctx); } else { retire_inbound_urb(ep, ctx); /* can be stopped during retire callback */ if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags))) - goto exit_clear; - - prepare_inbound_urb(ep, ctx); + return; } - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err == 0) - return; + ctx->data_packets = 0; + ++ctx; + if (ctx >= ep->urb + ep->nurbs) + ctx = &ep->urb[0]; - snd_printk(KERN_ERR "cannot submit urb (err = %d)\n", err); + if (snd_submit_urbs(ep, ctx) == 0) + return; //snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - -exit_clear: - clear_bit(ctx->index, &ep->active_mask); } /** @@ -574,7 +616,7 @@ static int data_ep_set_params(struct snd struct audioformat *fmt, struct snd_usb_endpoint *sync_ep) { - unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms; + unsigned int maxsize, i, urb_packs, max_packs, packs_per_ms; int is_playback = usb_pipeout(ep->pipe); int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels; @@ -593,8 +635,8 @@ static int data_ep_set_params(struct snd /* assume max. frequency is 25% higher than nominal */ ep->freqmax = ep->freqn + (ep->freqn >> 2); - maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3)) - >> (16 - ep->datainterval); + maxsize = ((ep->freqmax + 0xffff) >> (16 - ep->datainterval)) + * (frame_bits >> 3); /* but wMaxPacketSize might reduce this */ if (ep->maxpacksize && ep->maxpacksize < maxsize) { /* whatever fits into a max. size packet */ @@ -608,11 +650,21 @@ static int data_ep_set_params(struct snd else ep->curpacksize = maxsize; - if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) + if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) { packs_per_ms = 8 >> ep->datainterval; - else + + /* high speed needs 10 USB uframes on the queue at all times */ + ep->min_queued_packs = DIV_ROUND_UP(10, 8 / packs_per_ms); + max_packs = MAX_PACKS_HS; + } else { packs_per_ms = 1; + /* full speed needs one USB frame on the queue at all times */ + ep->min_queued_packs = 1; + max_packs = MAX_PACKS; + } + max_packs = min(max_packs, MAX_QUEUE * packs_per_ms); + if (is_playback && !snd_usb_endpoint_implicit_feedback_sink(ep)) { urb_packs = max(ep->chip->nrpacks, 1); urb_packs = min(urb_packs, (unsigned int) MAX_PACKS); @@ -625,41 +677,32 @@ static int data_ep_set_params(struct snd if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep)) urb_packs = min(urb_packs, 1U << sync_ep->syncinterval); - /* decide how many packets to be used */ - if (is_playback && !snd_usb_endpoint_implicit_feedback_sink(ep)) { - unsigned int minsize, maxpacks; - /* determine how small a packet can be */ - minsize = (ep->freqn >> (16 - ep->datainterval)) - * (frame_bits >> 3); - /* with sync from device, assume it can be 12% lower */ - if (sync_ep) - minsize -= minsize >> 3; - minsize = max(minsize, 1u); - total_packs = (period_bytes + minsize - 1) / minsize; - /* we need at least two URBs for queueing */ - if (total_packs < 2) { - total_packs = 2; - } else { - /* and we don't want too long a queue either */ - maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2); - total_packs = min(total_packs, maxpacks); - } - } else { - while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) - urb_packs >>= 1; - total_packs = MAX_URBS * urb_packs; - } + /* no point having an URB much longer than one period */ + urb_packs = min(urb_packs, 1 + period_bytes / maxsize); - ep->nurbs = (total_packs + urb_packs - 1) / urb_packs; - if (ep->nurbs > MAX_URBS) { - /* too much... */ - ep->nurbs = MAX_URBS; - total_packs = MAX_URBS * urb_packs; - } else if (ep->nurbs < 2) { - /* too little - we need at least two packets - * to ensure contiguous playback/capture - */ - ep->nurbs = 2; + /* + * We can't tell in advance how many URBs are needed to fill the + * USB hardware queue, because the number of packets per URB is + * variable (it depends on the period size and the device's clock + * frequency). Instead, get a worst-case overestimate by assuming + * the number of packets alternates between 1 and urb_packs. + * + * The total number of URBs needed is one higher than this, because + * the hardware queue must remain full even while one URB is + * completing (and therefore not on the queue). + * + * Recording endpoints with no sync always use the same number of + * packets per URB, so we can get a better estimate for them. + */ + if (is_playback || sync_ep) + ep->nurbs = 1 + 2 * DIV_ROUND_UP(ep->min_queued_packs, + urb_packs + 1); + else + ep->nurbs = 1 + DIV_ROUND_UP(ep->min_queued_packs, urb_packs); + + if (ep->nurbs * urb_packs > max_packs) { + /* too much -- URBs have too many packets */ + urb_packs = max_packs / ep->nurbs; } /* allocate and initialize data urbs */ @@ -667,8 +710,8 @@ static int data_ep_set_params(struct snd struct snd_urb_ctx *u = &ep->urb[i]; u->index = i; u->ep = ep; - u->packets = (i + 1) * total_packs / ep->nurbs - - i * total_packs / ep->nurbs; + u->packets = urb_packs; + u->data_packets = 0; u->buffer_size = maxsize * u->packets; if (fmt->fmt_type == UAC_FORMAT_TYPE_II) @@ -861,30 +904,14 @@ int snd_usb_endpoint_start(struct snd_us return 0; } - for (i = 0; i < ep->nurbs; i++) { - struct urb *urb = ep->urb[i].urb; - - if (snd_BUG_ON(!urb)) - goto __error; - - if (usb_pipeout(ep->pipe)) { - prepare_outbound_urb(ep, urb->context); - } else { - prepare_inbound_urb(ep, urb->context); - } - - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0) { - snd_printk(KERN_ERR "cannot submit urb %d, error %d: %s\n", - i, err, usb_error_string(err)); - goto __error; - } - set_bit(i, &ep->active_mask); - } - - return 0; + ep->queued_packs = 0; + ep->queued_urbs = 0; + ep->next_urb = 0; + ep->urb[0].data_packets = 0; + err = snd_submit_urbs(ep, &ep->urb[0]); + if (!err) + return 0; -__error: clear_bit(EP_FLAG_RUNNING, &ep->flags); ep->use_count--; deactivate_urbs(ep, false); -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html