Commit ab565f7e authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'fixes-for-v5.9-rc2' of...

Merge tag 'fixes-for-v5.9-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb

 into usb-linus

Felipe writes:

USB: fixes for v5.9-rc

Three ZLP fixes on dwc3 and a resource leak fix on the TCM gadget

Signed-off-by: default avatarFelipe Balbi <balbi@kernel.org>

* tag 'fixes-for-v5.9-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb:
  usb: dwc3: gadget: Handle ZLP for sg requests
  usb: dwc3: gadget: Fix handling ZLP
  usb: dwc3: gadget: Don't setup more than requested
  usb: gadget: f_tcm: Fix some resource leaks in some error paths
parents d5643d22 bc9a2e22
Loading
Loading
Loading
Loading
+89 −18
Original line number Diff line number Diff line
@@ -1054,27 +1054,25 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
 * dwc3_prepare_one_trb - setup one TRB from one request
 * @dep: endpoint for which this request is prepared
 * @req: dwc3_request pointer
 * @trb_length: buffer size of the TRB
 * @chain: should this TRB be chained to the next?
 * @node: only for isochronous endpoints. First TRB needs different type.
 */
static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
		struct dwc3_request *req, unsigned chain, unsigned node)
		struct dwc3_request *req, unsigned int trb_length,
		unsigned chain, unsigned node)
{
	struct dwc3_trb		*trb;
	unsigned int		length;
	dma_addr_t		dma;
	unsigned		stream_id = req->request.stream_id;
	unsigned		short_not_ok = req->request.short_not_ok;
	unsigned		no_interrupt = req->request.no_interrupt;
	unsigned		is_last = req->request.is_last;

	if (req->request.num_sgs > 0) {
		length = sg_dma_len(req->start_sg);
	if (req->request.num_sgs > 0)
		dma = sg_dma_address(req->start_sg);
	} else {
		length = req->request.length;
	else
		dma = req->request.dma;
	}

	trb = &dep->trb_pool[dep->trb_enqueue];

@@ -1086,7 +1084,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,

	req->num_trbs++;

	__dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
	__dwc3_prepare_one_trb(dep, trb, dma, trb_length, chain, node,
			stream_id, short_not_ok, no_interrupt, is_last);
}

@@ -1096,16 +1094,27 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
	struct scatterlist *sg = req->start_sg;
	struct scatterlist *s;
	int		i;

	unsigned int length = req->request.length;
	unsigned int remaining = req->request.num_mapped_sgs
		- req->num_queued_sgs;

	/*
	 * If we resume preparing the request, then get the remaining length of
	 * the request and resume where we left off.
	 */
	for_each_sg(req->request.sg, s, req->num_queued_sgs, i)
		length -= sg_dma_len(s);

	for_each_sg(sg, s, remaining, i) {
		unsigned int length = req->request.length;
		unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
		unsigned int rem = length % maxp;
		unsigned int trb_length;
		unsigned chain = true;

		trb_length = min_t(unsigned int, length, sg_dma_len(s));

		length -= trb_length;

		/*
		 * IOMMU driver is coalescing the list of sgs which shares a
		 * page boundary into one and giving it to USB driver. With
@@ -1113,7 +1122,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
		 * sgs passed. So mark the chain bit to false if it isthe last
		 * mapped sg.
		 */
		if (i == remaining - 1)
		if ((i == remaining - 1) || !length)
			chain = false;

		if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) {
@@ -1123,7 +1132,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
			req->needs_extra_trb = true;

			/* prepare normal TRB */
			dwc3_prepare_one_trb(dep, req, true, i);
			dwc3_prepare_one_trb(dep, req, trb_length, true, i);

			/* Now prepare one extra TRB to align transfer size */
			trb = &dep->trb_pool[dep->trb_enqueue];
@@ -1134,8 +1143,39 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
					req->request.short_not_ok,
					req->request.no_interrupt,
					req->request.is_last);
		} else if (req->request.zero && req->request.length &&
			   !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
			   !rem && !chain) {
			struct dwc3	*dwc = dep->dwc;
			struct dwc3_trb	*trb;

			req->needs_extra_trb = true;

			/* Prepare normal TRB */
			dwc3_prepare_one_trb(dep, req, trb_length, true, i);

			/* Prepare one extra TRB to handle ZLP */
			trb = &dep->trb_pool[dep->trb_enqueue];
			req->num_trbs++;
			__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
					       !req->direction, 1,
					       req->request.stream_id,
					       req->request.short_not_ok,
					       req->request.no_interrupt,
					       req->request.is_last);

			/* Prepare one more TRB to handle MPS alignment */
			if (!req->direction) {
				trb = &dep->trb_pool[dep->trb_enqueue];
				req->num_trbs++;
				__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp,
						       false, 1, req->request.stream_id,
						       req->request.short_not_ok,
						       req->request.no_interrupt,
						       req->request.is_last);
			}
		} else {
			dwc3_prepare_one_trb(dep, req, chain, i);
			dwc3_prepare_one_trb(dep, req, trb_length, chain, i);
		}

		/*
@@ -1150,6 +1190,16 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,

		req->num_queued_sgs++;

		/*
		 * The number of pending SG entries may not correspond to the
		 * number of mapped SG entries. If all the data are queued, then
		 * don't include unused SG entries.
		 */
		if (length == 0) {
			req->num_pending_sgs -= req->request.num_mapped_sgs - req->num_queued_sgs;
			break;
		}

		if (!dwc3_calc_trbs_left(dep))
			break;
	}
@@ -1169,7 +1219,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
		req->needs_extra_trb = true;

		/* prepare normal TRB */
		dwc3_prepare_one_trb(dep, req, true, 0);
		dwc3_prepare_one_trb(dep, req, length, true, 0);

		/* Now prepare one extra TRB to align transfer size */
		trb = &dep->trb_pool[dep->trb_enqueue];
@@ -1180,6 +1230,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
				req->request.no_interrupt,
				req->request.is_last);
	} else if (req->request.zero && req->request.length &&
		   !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
		   (IS_ALIGNED(req->request.length, maxp))) {
		struct dwc3	*dwc = dep->dwc;
		struct dwc3_trb	*trb;
@@ -1187,18 +1238,29 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
		req->needs_extra_trb = true;

		/* prepare normal TRB */
		dwc3_prepare_one_trb(dep, req, true, 0);
		dwc3_prepare_one_trb(dep, req, length, true, 0);

		/* Now prepare one extra TRB to handle ZLP */
		/* Prepare one extra TRB to handle ZLP */
		trb = &dep->trb_pool[dep->trb_enqueue];
		req->num_trbs++;
		__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
				!req->direction, 1, req->request.stream_id,
				req->request.short_not_ok,
				req->request.no_interrupt,
				req->request.is_last);

		/* Prepare one more TRB to handle MPS alignment for OUT */
		if (!req->direction) {
			trb = &dep->trb_pool[dep->trb_enqueue];
			req->num_trbs++;
			__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp,
					       false, 1, req->request.stream_id,
					       req->request.short_not_ok,
					       req->request.no_interrupt,
					       req->request.is_last);
		}
	} else {
		dwc3_prepare_one_trb(dep, req, false, 0);
		dwc3_prepare_one_trb(dep, req, length, false, 0);
	}
}

@@ -2671,8 +2733,17 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
				status);

	if (req->needs_extra_trb) {
		unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);

		ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
				status);

		/* Reclaim MPS padding TRB for ZLP */
		if (!req->direction && req->request.zero && req->request.length &&
		    !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
		    (IS_ALIGNED(req->request.length, maxp)))
			ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status);

		req->needs_extra_trb = false;
	}

+4 −3
Original line number Diff line number Diff line
@@ -753,12 +753,13 @@ static int uasp_alloc_stream_res(struct f_uas *fu, struct uas_stream *stream)
		goto err_sts;

	return 0;

err_sts:
	usb_ep_free_request(fu->ep_status, stream->req_status);
	stream->req_status = NULL;
err_out:
	usb_ep_free_request(fu->ep_out, stream->req_out);
	stream->req_out = NULL;
err_out:
	usb_ep_free_request(fu->ep_in, stream->req_in);
	stream->req_in = NULL;
out:
	return -ENOMEM;
}