Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 04a378f3 authored by Thomas Pugliese's avatar Thomas Pugliese Committed by Greg Kroah-Hartman
Browse files

usb: wusbcore: combine iso transfer result frame reads when possible



When reading the transfer result data for an isochronous in request, if
the current frame actual_length is contiguous with the next frame and
actual_length is a multiple of the DTI endpoint max packet size, combine
the current frame with the next frame in a single URB.  This reduces the
number of URBs that must be submitted in that case which increases
performance and reduces CPU interrupt overhead.

Signed-off-by: default avatarThomas Pugliese <thomas.pugliese@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 7338a065
Loading
Loading
Loading
Loading
+76 −30
Original line number Diff line number Diff line
@@ -2160,22 +2160,59 @@ static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer,
}

/* Populate the wa->buf_in_urb based on the current isoc transfer state. */
static void __wa_populate_buf_in_urb_isoc(struct wahc *wa, struct wa_xfer *xfer,
	struct wa_seg *seg, int curr_iso_frame)
static int __wa_populate_buf_in_urb_isoc(struct wahc *wa, struct wa_xfer *xfer,
	struct wa_seg *seg)
{
	int urb_start_frame = seg->isoc_frame_index + seg->isoc_frame_offset;
	int seg_index, total_len = 0, urb_frame_index = urb_start_frame;
	struct usb_iso_packet_descriptor *iso_frame_desc =
						xfer->urb->iso_frame_desc;
	const int dti_packet_size = usb_endpoint_maxp(wa->dti_epd);
	int next_frame_contiguous;
	struct usb_iso_packet_descriptor *iso_frame;

	BUG_ON(wa->buf_in_urb->status == -EINPROGRESS);

	/*
	 * If the current frame actual_length is contiguous with the next frame
	 * and actual_length is a multiple of the DTI endpoint max packet size,
	 * combine the current frame with the next frame in a single URB.  This
	 * reduces the number of URBs that must be submitted in that case.
	 */
	seg_index = seg->isoc_frame_index;
	do {
		next_frame_contiguous = 0;

		iso_frame = &iso_frame_desc[urb_frame_index];
		total_len += iso_frame->actual_length;
		++urb_frame_index;
		++seg_index;

		if (seg_index < seg->isoc_frame_count) {
			struct usb_iso_packet_descriptor *next_iso_frame;

			next_iso_frame = &iso_frame_desc[urb_frame_index];

			if ((iso_frame->offset + iso_frame->actual_length) ==
				next_iso_frame->offset)
				next_frame_contiguous = 1;
		}
	} while (next_frame_contiguous
			&& ((iso_frame->actual_length % dti_packet_size) == 0));

	/* this should always be 0 before a resubmit. */
	wa->buf_in_urb->num_mapped_sgs	= 0;
	wa->buf_in_urb->transfer_dma = xfer->urb->transfer_dma +
		xfer->urb->iso_frame_desc[curr_iso_frame].offset;
	wa->buf_in_urb->transfer_buffer_length =
		xfer->urb->iso_frame_desc[curr_iso_frame].actual_length;
		iso_frame_desc[urb_start_frame].offset;
	wa->buf_in_urb->transfer_buffer_length = total_len;
	wa->buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
	wa->buf_in_urb->transfer_buffer = NULL;
	wa->buf_in_urb->sg = NULL;
	wa->buf_in_urb->num_sgs = 0;
	wa->buf_in_urb->context = seg;

	/* return the number of frames included in this URB. */
	return seg_index - seg->isoc_frame_index;
}

/* Populate the wa->buf_in_urb based on the current transfer state. */
@@ -2458,19 +2495,20 @@ static int wa_process_iso_packet_status(struct wahc *wa, struct urb *urb)
	}

	if (xfer->is_inbound && data_frame_count) {
		int result;
		int result, urb_frame_count;

		seg->isoc_frame_index = first_frame_index;
		/* submit a read URB for the first frame with data. */
		__wa_populate_buf_in_urb_isoc(wa, xfer, seg,
			seg->isoc_frame_index + seg->isoc_frame_offset);
		urb_frame_count = __wa_populate_buf_in_urb_isoc(wa, xfer, seg);
		/* advance index to start of next read URB. */
		seg->isoc_frame_index += urb_frame_count;

		result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC);
		if (result < 0) {
			dev_err(dev, "DTI Error: Could not submit buf in URB (%d)",
				result);
			wa_reset_all(wa);
		} else if (data_frame_count > 1)
		} else if (data_frame_count > urb_frame_count)
			/* If we need to read multiple frames, set DTI busy. */
			dti_busy = 1;
	} else {
@@ -2511,8 +2549,9 @@ static void wa_buf_in_cb(struct urb *urb)
	struct wahc *wa;
	struct device *dev;
	struct wa_rpipe *rpipe;
	unsigned rpipe_ready = 0, seg_index, isoc_data_frame_count = 0;
	unsigned rpipe_ready = 0, isoc_data_frame_count = 0;
	unsigned long flags;
	int resubmit_dti = 0;
	u8 done = 0;

	/* free the sg if it was used. */
@@ -2524,17 +2563,16 @@ static void wa_buf_in_cb(struct urb *urb)
	dev = &wa->usb_iface->dev;

	if (usb_pipeisoc(xfer->urb->pipe)) {
		/*
		 * Find the next isoc frame with data.  Bail out after
		 * isoc_data_frame_count > 1 since there is no need to walk
		 * the entire frame array.  We just need to know if
		 * isoc_data_frame_count is 0, 1, or >1.
		 */
		seg_index = seg->isoc_frame_index + 1;
		while ((seg_index < seg->isoc_frame_count)
			&& (isoc_data_frame_count <= 1)) {
		struct usb_iso_packet_descriptor *iso_frame_desc =
			xfer->urb->iso_frame_desc;
		int	seg_index;

		/*
		 * Find the next isoc frame with data and count how many
		 * frames with data remain.
		 */
		seg_index = seg->isoc_frame_index;
		while (seg_index < seg->isoc_frame_count) {
			const int urb_frame_index =
				seg->isoc_frame_offset + seg_index;

@@ -2555,16 +2593,28 @@ static void wa_buf_in_cb(struct urb *urb)

		seg->result += urb->actual_length;
		if (isoc_data_frame_count > 0) {
			int result;
			/* submit a read URB for the first frame with data. */
			__wa_populate_buf_in_urb_isoc(wa, xfer, seg,
				seg->isoc_frame_index + seg->isoc_frame_offset);
			int result, urb_frame_count;

			/* submit a read URB for the next frame with data. */
			urb_frame_count = __wa_populate_buf_in_urb_isoc(wa,
				 xfer, seg);
			/* advance index to start of next read URB. */
			seg->isoc_frame_index += urb_frame_count;
			result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC);
			if (result < 0) {
				dev_err(dev, "DTI Error: Could not submit buf in URB (%d)",
					result);
				wa_reset_all(wa);
			}
			/*
			 * If we are in this callback and
			 * isoc_data_frame_count > 0, it means that the dti_urb
			 * submission was delayed in wa_dti_cb.  Once
			 * we submit the last buf_in_urb, we can submit the
			 * delayed dti_urb.
			 */
			  resubmit_dti = (isoc_data_frame_count ==
							urb_frame_count);
		} else {
			rpipe = xfer->ep->hcpriv;
			dev_dbg(dev,
@@ -2585,6 +2635,7 @@ static void wa_buf_in_cb(struct urb *urb)
	case -ENOENT:		/* as it was done by the who unlinked us */
		break;
	default:		/* Other errors ... */
		resubmit_dti = 1;
		spin_lock_irqsave(&xfer->lock, flags);
		rpipe = xfer->ep->hcpriv;
		if (printk_ratelimit())
@@ -2607,13 +2658,8 @@ static void wa_buf_in_cb(struct urb *urb)
		if (rpipe_ready)
			wa_xfer_delayed_run(rpipe);
	}
	/*
	 * If we are in this callback and isoc_data_frame_count > 0, it means
	 * that the dti_urb submission was delayed in wa_dti_cb.  Once
	 * isoc_data_frame_count gets to 1, we can submit the deferred URB
	 * since the last buf_in_urb was just submitted.
	 */
	if (isoc_data_frame_count == 1) {

	if (resubmit_dti) {
		int result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC);
		if (result < 0) {
			dev_err(dev, "DTI Error: Could not submit DTI URB (%d)\n",