Re: USB changes for UAS

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

 



On Sat, 1 May 2010, Matthew Wilcox wrote:

> > > The patch you did to EHCI (as1300 / commit 40f8db8f8f) does look quite
> > > straightforward to port to the uhci-q.c and ohci-q.c file.  On the one
> > > hand, that's a good thing, because I can just do that (er, if I can figure
> > > out a way to test either of them).
> > 
> > Testing is easy -- just see if your mass storage devices still work.  
> > usb-storage calls usb_sg_*, which will use the HCD's native SG support.
> 
> Easy ... if you have the hardware.  I need to check my hubs to see if
> I have one that's USB 1.1 only, so I can force an EHCI controller into
> UHCI mode.

You don't need an extra hub.  Just rmmod ehci-hcd.

> > >  On the other hand, it's not taking
> > > advantage of EHCI's native ability to do scatter-gather in a single qtd if
> > > the pages are virtually contiguous (see pages 86-88 of the EHCI 1.0 spec).
> > > 
> > > So EHCI could be further enhanced ... if it's worth it.
> > 
> > Hmm, maybe...  If it's not too hard to do.  I'll have to look at the 
> > code.
> 
> I think the cleanest way to do it is to split qtd_fill into qtd_fill_buf
> and qtd_fill_sg; use the former for the usb_pipecontrol and one_more
> cases; and the latter in the main loop.  qtd_fill_buf could be quite simple
> since it's never going to use multiple pages.  qtd_fill_sg would be pretty
> complex though.
> 
> I have no idea how much improvement we'd see from using multiple pages
> in a qtd instead of using one qtd per page though.

You mean multiple scatterlist elements per qtd -- we already can have 
multiple pages per qtd if a scatterlist element is more than one page 
long.

I don't know how much difference it would make either, probably not
anything measurable.  But if you want to play around with it, here's a
totally untested patch.  It changes qtd_fill fairly drastically (IMO
the overall result is a simplification), but nothing else needed much
work.  (It also fixes a bug in the toggle computation for control
transfers; apparently that pathway has never been used.)

Alan Stern


Index: usb-2.6/drivers/usb/host/ehci-q.c
===================================================================
--- usb-2.6.orig/drivers/usb/host/ehci-q.c
+++ usb-2.6/drivers/usb/host/ehci-q.c
@@ -44,40 +44,59 @@
 
 static int
 qtd_fill(struct ehci_hcd *ehci, struct ehci_qtd *qtd, dma_addr_t buf,
-		  size_t len, int token, int maxpacket)
+		  size_t len, int token, int maxpacket, int *index)
 {
-	int	i, count;
-	u64	addr = buf;
+	int	i;
+	int	count, n;
+	u64	addr;
+
+	n = buf & 0x0fff;	/* start position in the first page */
+	i = *index;
+	*index = 0;		/* assume the next transfer can't be merged */
+	if (i == 0) {
+		qtd->hw_token = cpu_to_hc32(ehci, token);
+	} else if (unlikely(n)) {
+		/* Can't merge buffers if they aren't "virtually contiguous":
+		 * This one must start at a page boundary (and the previous
+		 * one must have ended at a page boundary) (4.10.6).
+		 */
+		return 0;
+	}
 
 	/* one buffer entry per 4K ... first might be short or unaligned */
-	qtd->hw_buf[0] = cpu_to_hc32(ehci, (u32)addr);
-	qtd->hw_buf_hi[0] = cpu_to_hc32(ehci, (u32)(addr >> 32));
-	count = 0x1000 - (buf & 0x0fff);	/* rest of that page */
-	if (likely (len < count))		/* ... iff needed */
-		count = len;
-	else {
-		buf +=  0x1000;
-		buf &= ~0x0fff;
+	n = 0x1000 - n;		/* rest of the first page */
+	count = 0;
 
-		/* per-qtd limit: from 16K to 20K (best alignment) */
-		for (i = 1; count < len && i < 5; i++) {
-			addr = buf;
-			qtd->hw_buf[i] = cpu_to_hc32(ehci, (u32)addr);
-			qtd->hw_buf_hi[i] = cpu_to_hc32(ehci,
-					(u32)(addr >> 32));
-			buf += 0x1000;
-			if ((count + 0x1000) < len)
-				count += 0x1000;
-			else
-				count = len;
-		}
-
-		/* short packets may only terminate transfers */
-		if (count != len)
-			count -= (count % maxpacket);
+	/* per-qtd limit: from 16K to 20K (best alignment) */
+	while (i < 5) {
+		addr = buf;
+		qtd->hw_buf[i] = cpu_to_hc32(ehci, (u32)addr);
+		qtd->hw_buf_hi[i] = cpu_to_hc32(ehci, (u32)(addr >> 32));
+		++i;
+		buf += n;
+		count += n;
+		if (count >= len) {
+			/* If this transfer ended at a page boundary,
+			 * the next transfer may also go in this qtd.
+			 */
+			if (count == len && i < 5)
+				*index = i;
+			count = len;
+			break;
+		}
+		n = 0x1000;
 	}
-	qtd->hw_token = cpu_to_hc32(ehci, (count << 16) | token);
-	qtd->length = count;
+
+	/* Short packets may only terminate transfers.
+	 * This matters only if the buffer is > 16KB and not aligned
+	 * on a maxpacket boundary -- so it should never matter.
+	 */
+	if (count != len)
+		count -= (count % maxpacket);
+
+	qtd->length += count;
+	qtd->hw_token &= ~cpu_to_hc32(ehci, (0x7fff << 16));
+	qtd->hw_token |= cpu_to_hc32(ehci, (qtd->length << 16));
 
 	return count;
 }
@@ -619,6 +638,7 @@ qh_urb_transaction (
 	int			len, this_sg_len, maxpacket;
 	int			is_input;
 	u32			token;
+	int			index;
 	int			i;
 	struct scatterlist	*sg;
 
@@ -630,6 +650,7 @@ qh_urb_transaction (
 		return NULL;
 	list_add_tail (&qtd->qtd_list, head);
 	qtd->urb = urb;
+	index = 0;
 
 	token = QTD_STS_ACTIVE;
 	token |= (EHCI_TUNE_CERR << 10);
@@ -641,7 +662,7 @@ qh_urb_transaction (
 		/* SETUP pid */
 		qtd_fill(ehci, qtd, urb->setup_dma,
 				sizeof (struct usb_ctrlrequest),
-				token | (2 /* "setup" */ << 8), 8);
+				token | (2 /* "setup" */ << 8), 8, &index);
 
 		/* ... and always at least one more pid */
 		token ^= QTD_TOGGLE;
@@ -652,6 +673,7 @@ qh_urb_transaction (
 		qtd->urb = urb;
 		qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
 		list_add_tail (&qtd->qtd_list, head);
+		index = 0;
 
 		/* for zero length DATA stages, STATUS is always IN */
 		if (len == 0)
@@ -691,7 +713,7 @@ qh_urb_transaction (
 		int this_qtd_len;
 
 		this_qtd_len = qtd_fill(ehci, qtd, buf, this_sg_len, token,
-				maxpacket);
+				maxpacket, &index);
 		this_sg_len -= this_qtd_len;
 		len -= this_qtd_len;
 		buf += this_qtd_len;
@@ -705,7 +727,7 @@ qh_urb_transaction (
 			qtd->hw_alt_next = ehci->async->hw->hw_alt_next;
 
 		/* qh makes control packets use qtd toggle; maybe switch it */
-		if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
+		if (maxpacket & (this_qtd_len + (maxpacket - 1)))
 			token ^= QTD_TOGGLE;
 
 		if (likely(this_sg_len <= 0)) {
@@ -716,13 +738,15 @@ qh_urb_transaction (
 			this_sg_len = min_t(int, sg_dma_len(sg), len);
 		}
 
-		qtd_prev = qtd;
-		qtd = ehci_qtd_alloc (ehci, flags);
-		if (unlikely (!qtd))
-			goto cleanup;
-		qtd->urb = urb;
-		qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
-		list_add_tail (&qtd->qtd_list, head);
+		if (!index) {
+			qtd_prev = qtd;
+			qtd = ehci_qtd_alloc(ehci, flags);
+			if (unlikely(!qtd))
+				goto cleanup;
+			qtd->urb = urb;
+			qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
+			list_add_tail(&qtd->qtd_list, head);
+		}
 	}
 
 	/*
@@ -758,9 +782,10 @@ qh_urb_transaction (
 			qtd->urb = urb;
 			qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
 			list_add_tail (&qtd->qtd_list, head);
+			index = 0;
 
 			/* never any data in such packets */
-			qtd_fill(ehci, qtd, 0, 0, token, 0);
+			qtd_fill(ehci, qtd, 0, 0, token, 0, &index);
 		}
 	}
 

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