[RFC v2 6/7] xHCI: wait for dequeue pointer move pass link TRB

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When trying to expand a ring, if enqueue pointer is behind dequeue pointer
and they're in the same segment, we can not expand the ring until the
dequeue pointer move pass the link TRB. This is because if the hardware
has not cached the link TRB, it will jump to the new segments, but the
data there is not right and the hardware may stop if the cycle_bit
indicates it's software owned.

Wait for the dequeue pointer move pass the link TRB, and then it's safe
to expand the ring.

Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx>
---
 drivers/usb/host/xhci-mem.c  |    1 +
 drivers/usb/host/xhci-ring.c |   81 +++++++++++++++++++++++++++++++-----------
 drivers/usb/host/xhci.c      |    8 ++--
 drivers/usb/host/xhci.h      |   14 +++++--
 4 files changed, 75 insertions(+), 29 deletions(-)

diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 1e68c57..97db9bb 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -185,6 +185,7 @@ static void xhci_initialize_ring_info(struct xhci_ring *ring,
 	/* Not necessary for new rings, but needed for re-initialized rings */
 	ring->enq_updates = 0;
 	ring->deq_updates = 0;
+	ring->notify_link = 0;
 }
 
 /* Allocate segments and link them for a ring */
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 34c5495..043a700 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -169,6 +169,14 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer
 		ring->deq_seg = ring->deq_seg->next;
 		ring->dequeue = ring->deq_seg->trbs;
 		next = ring->dequeue;
+		/*
+		 * Complete pass_link if ring expansion is waiting for dequeue
+		 * pointer pass link TRB.
+		 */
+		if (ring->notify_link) {
+			complete(&ring->pass_link);
+			ring->notify_link = 0;
+		}
 	}
 	addr = (unsigned long long) xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue);
 }
@@ -2392,9 +2400,10 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
  */
 static int prepare_ring(struct xhci_hcd *xhci, struct xhci_virt_device *xdev,
 		struct xhci_ring *ep_ring, u32 ep_state, unsigned int num_trbs,
-		bool isoc, gfp_t mem_flags)
+		bool isoc, gfp_t mem_flags, unsigned long flags)
 {
 	unsigned int num_trbs_needed;
+	int timeleft;
 
 	/* Make sure the endpoint has been added to xHC schedule */
 	switch (ep_state) {
@@ -2433,11 +2442,31 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_virt_device *xdev,
 			return -ENOMEM;
 		}
 
+		/*
+		 * If enqueue pointer is behind dequeue pointer and they're
+		 * in the same segment, wait for the dequeue pointer to pass
+		 * the link TRB.
+		 */
 		if (ep_ring->enq_seg == ep_ring->deq_seg &&
 				ep_ring->dequeue > ep_ring->enqueue) {
-			xhci_err(xhci, "Can not expand the ring while dequeue "
-				"pointer has not passed the link TRB\n");
-			return -ENOMEM;
+			xhci_dbg(xhci, "Waiting for dequeue pointer pass "
+					"link TRB\n");
+			ep_ring->notify_link = 1;
+			init_completion(&ep_ring->pass_link);
+			spin_unlock_irqrestore(&xhci->lock, flags);
+
+			/* Wait for the dequeue pointer pass link TRB */
+			timeleft = wait_for_completion_interruptible_timeout(
+					&ep_ring->pass_link,
+					USB_CTRL_SET_TIMEOUT);
+			if (timeleft <= 0) {
+				xhci_warn(xhci, "%s while waiting for passing "
+						"link TRB\n", timeleft == 0 ?
+						"Timeout" : "Signal");
+				spin_lock_irqsave(&xhci->lock, flags);
+				return -ETIME;
+			}
+			spin_lock_irqsave(&xhci->lock, flags);
 		}
 
 		xhci_dbg(xhci, "ERROR no room on ep ring, "
@@ -2495,7 +2524,8 @@ static int prepare_transfer(struct xhci_hcd *xhci,
 		struct urb *urb,
 		unsigned int td_index,
 		bool isoc,
-		gfp_t mem_flags)
+		gfp_t mem_flags,
+		unsigned long flags)
 {
 	int ret;
 	struct urb_priv *urb_priv;
@@ -2512,7 +2542,7 @@ static int prepare_transfer(struct xhci_hcd *xhci,
 
 	ret = prepare_ring(xhci, xdev, ep_ring,
 			   le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK,
-			   num_trbs, isoc, mem_flags);
+			   num_trbs, isoc, mem_flags, flags);
 	if (ret)
 		return ret;
 
@@ -2624,7 +2654,8 @@ static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id,
  * transmit.
  */
 int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
-		struct urb *urb, int slot_id, unsigned int ep_index)
+		struct urb *urb, int slot_id, unsigned int ep_index,
+		unsigned long flags)
 {
 	struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci,
 			xhci->devs[slot_id]->out_ctx, ep_index);
@@ -2655,7 +2686,8 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 				urb->dev->speed == USB_SPEED_FULL)
 			urb->interval /= 8;
 	}
-	return xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index);
+	return xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index,
+					flags);
 }
 
 /*
@@ -2707,7 +2739,8 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len,
 }
 
 static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
-		struct urb *urb, int slot_id, unsigned int ep_index)
+		struct urb *urb, int slot_id, unsigned int ep_index,
+		unsigned long flags)
 {
 	struct xhci_ring *ep_ring;
 	unsigned int num_trbs;
@@ -2735,7 +2768,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 
 	trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id],
 			ep_index, urb->stream_id,
-			num_trbs, urb, 0, false, mem_flags);
+			num_trbs, urb, 0, false, mem_flags, flags);
 	if (trb_buff_len < 0)
 		return trb_buff_len;
 
@@ -2869,7 +2902,8 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 
 /* This is very similar to what ehci-q.c qtd_fill() does */
 int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
-		struct urb *urb, int slot_id, unsigned int ep_index)
+		struct urb *urb, int slot_id, unsigned int ep_index,
+		unsigned long flags)
 {
 	struct xhci_ring *ep_ring;
 	struct urb_priv *urb_priv;
@@ -2886,7 +2920,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 	u64 addr;
 
 	if (urb->num_sgs)
-		return queue_bulk_sg_tx(xhci, mem_flags, urb, slot_id, ep_index);
+		return queue_bulk_sg_tx(xhci, mem_flags, urb, slot_id,
+					ep_index, flags);
 
 	ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
 	if (!ep_ring)
@@ -2921,7 +2956,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 
 	ret = prepare_transfer(xhci, xhci->devs[slot_id],
 			ep_index, urb->stream_id,
-			num_trbs, urb, 0, false, mem_flags);
+			num_trbs, urb, 0, false, mem_flags, flags);
 	if (ret < 0)
 		return ret;
 
@@ -3016,7 +3051,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 
 /* Caller must have locked xhci->lock */
 int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
-		struct urb *urb, int slot_id, unsigned int ep_index)
+		struct urb *urb, int slot_id, unsigned int ep_index,
+		unsigned long flags)
 {
 	struct xhci_ring *ep_ring;
 	int num_trbs;
@@ -3053,7 +3089,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 		num_trbs++;
 	ret = prepare_transfer(xhci, xhci->devs[slot_id],
 			ep_index, urb->stream_id,
-			num_trbs, urb, 0, false, mem_flags);
+			num_trbs, urb, 0, false, mem_flags, flags);
 	if (ret < 0)
 		return ret;
 
@@ -3210,7 +3246,8 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci,
 
 /* This is for isoc transfer */
 static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
-		struct urb *urb, int slot_id, unsigned int ep_index)
+		struct urb *urb, int slot_id, unsigned int ep_index,
+		unsigned long flags)
 {
 	struct xhci_ring *ep_ring;
 	struct urb_priv *urb_priv;
@@ -3272,7 +3309,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 
 		ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index,
 				urb->stream_id, trbs_per_td, urb, i, true,
-				mem_flags);
+				mem_flags, flags);
 		if (ret < 0) {
 			if (i == 0)
 				return ret;
@@ -3400,7 +3437,8 @@ cleanup:
  * Always assume URB_ISO_ASAP set, and NEVER use urb->start_frame as input.
  */
 int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
-		struct urb *urb, int slot_id, unsigned int ep_index)
+		struct urb *urb, int slot_id, unsigned int ep_index,
+		unsigned long flags)
 {
 	struct xhci_virt_device *xdev;
 	struct xhci_ring *ep_ring;
@@ -3425,7 +3463,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
 	 */
 	ret = prepare_ring(xhci, xdev, ep_ring,
 				le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK,
-				num_trbs, true, mem_flags);
+				num_trbs, true, mem_flags, flags);
 	if (ret)
 		return ret;
 
@@ -3461,7 +3499,8 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
 				urb->dev->speed == USB_SPEED_FULL)
 			urb->interval /= 8;
 	}
-	return xhci_queue_isoc_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index);
+	return xhci_queue_isoc_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index,
+					flags);
 }
 
 /****		Command Ring Operations		****/
@@ -3484,7 +3523,7 @@ static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2,
 		reserved_trbs++;
 
 	ret = prepare_ring(xhci, NULL, xhci->cmd_ring, EP_STATE_RUNNING,
-			reserved_trbs, false, GFP_ATOMIC);
+			reserved_trbs, false, GFP_ATOMIC, 0);
 	if (ret < 0) {
 		xhci_err(xhci, "ERR: No room for command on command ring\n");
 		if (command_must_succeed)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index e70ddf2..4c858b9 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1152,7 +1152,7 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
 		if (xhci->xhc_state & XHCI_STATE_DYING)
 			goto dying;
 		ret = xhci_queue_ctrl_tx(xhci, GFP_ATOMIC, urb,
-				slot_id, ep_index);
+				slot_id, ep_index, flags);
 		if (ret)
 			goto free_priv;
 		spin_unlock_irqrestore(&xhci->lock, flags);
@@ -1173,7 +1173,7 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
 			ret = -EINVAL;
 		} else {
 			ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb,
-					slot_id, ep_index);
+					slot_id, ep_index, flags);
 		}
 		if (ret)
 			goto free_priv;
@@ -1183,7 +1183,7 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
 		if (xhci->xhc_state & XHCI_STATE_DYING)
 			goto dying;
 		ret = xhci_queue_intr_tx(xhci, GFP_ATOMIC, urb,
-				slot_id, ep_index);
+				slot_id, ep_index, flags);
 		if (ret)
 			goto free_priv;
 		spin_unlock_irqrestore(&xhci->lock, flags);
@@ -1192,7 +1192,7 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
 		if (xhci->xhc_state & XHCI_STATE_DYING)
 			goto dying;
 		ret = xhci_queue_isoc_tx_prepare(xhci, GFP_ATOMIC, urb,
-				slot_id, ep_index);
+				slot_id, ep_index, flags);
 		if (ret)
 			goto free_priv;
 		spin_unlock_irqrestore(&xhci->lock, flags);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 52c319b..986d146 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1274,6 +1274,11 @@ struct xhci_ring {
 	unsigned int		num_segs;
 	unsigned int		num_trbs_free;
 	bool			last_td_was_short;
+	/* Notify driver if dequeue pointer pass the link TRB.
+	 * For ring expansion use.
+	 */
+	struct completion	pass_link;
+	unsigned		notify_link:1;
 };
 
 struct xhci_erst_entry {
@@ -1719,13 +1724,14 @@ int xhci_queue_vendor_command(struct xhci_hcd *xhci,
 int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id,
 		unsigned int ep_index, int suspend);
 int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
-		int slot_id, unsigned int ep_index);
+		int slot_id, unsigned int ep_index, unsigned long flags);
 int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
-		int slot_id, unsigned int ep_index);
+		int slot_id, unsigned int ep_index, unsigned long flags);
 int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
-		int slot_id, unsigned int ep_index);
+		int slot_id, unsigned int ep_index, unsigned long flags);
 int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
-		struct urb *urb, int slot_id, unsigned int ep_index);
+		struct urb *urb, int slot_id, unsigned int ep_index,
+		unsigned long flags);
 int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
 		u32 slot_id, bool command_must_succeed);
 int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
-- 
1.7.4.1


--
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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux