kernel panic trying to utilize a da(4)/umass(4) device with ohci(4)

Ian Dowse iedowse at maths.tcd.ie
Thu Nov 20 01:12:04 PST 2003


In message <200311200326.hAK3Qqvf024651 at green.bikeshed.org>, "Brian F. Feldman"
 writes:
>Jeez, it's been broken a year and it's almost 5.2-RELEASE now.  Does anyone 
>have ANY leads on these problems?  I know precisely nothing about how my USB 
>hardware is supposed to work, but this OHCI+EHCI stuff definitely doesn't, 
>and it's really not uncommon at all.  Is it unbroken in NetBSD currently?

I had some success with this patch:

Index: usb_mem.c
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usb_mem.c,v
retrieving revision 1.5
diff -u -r1.5 usb_mem.c
--- usb_mem.c	4 Oct 2003 22:13:21 -0000	1.5
+++ usb_mem.c	27 Oct 2003 15:39:03 -0000
@@ -142,7 +142,8 @@
 	s = splusb();
 	/* First check the free list. */
 	for (p = LIST_FIRST(&usb_blk_freelist); p; p = LIST_NEXT(p, next)) {
-		if (p->tag == tag && p->size >= size && p->align >= align) {
+		if (p->tag == tag && p->size >= size && p->size < size * 2 &&
+		    p->align >= align) {
 			LIST_REMOVE(p, next);
 			usb_blk_nfree--;
 			splx(s);

It seems that since the conversion to busdma, the USB code can end
up attempting to use contigmalloc() to allocate multi-page regions
from an interrupt thread(!). The above doesn't fix that; it just
prevents successful large (e.g 64k) contiguous allocations from
being wasted when a much smaller amount of space is needed.

With the above, I was able to use ohci+ehci fairly reliably on a
Soekris box with a large USB2 disk attached via a cardbus USB2
adaptor. I also have a few other local patches that may help too -
some of them are below:

Ian

Index: ohci.c
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ohci.c,v
retrieving revision 1.132
diff -u -r1.132 ohci.c
--- ohci.c	24 Aug 2003 17:55:54 -0000	1.132
+++ ohci.c	21 Sep 2003 15:28:27 -0000
@@ -1405,12 +1405,13 @@
 			if (std->flags & OHCI_ADD_LEN)
 				xfer->actlen += len;
 			if (std->flags & OHCI_CALL_DONE) {
+				ohci_free_std(sc, std); /* XXX */
 				xfer->status = USBD_NORMAL_COMPLETION;
 				s = splusb();
 				usb_transfer_complete(xfer);
 				splx(s);
-			}
-			ohci_free_std(sc, std);
+			} else
+				ohci_free_std(sc, std);
 		} else {
 			/*
 			 * Endpoint is halted.  First unlink all the TDs
@@ -2246,6 +2247,7 @@
 		usb_uncallout(xfer->timeout_handle, ohci_timeout, xfer);
 		usb_transfer_complete(xfer);
 		splx(s);
+		return;
 	}
 
 	if (xfer->device->bus->intr_context || !curproc)
Index: usbdi.c
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usbdi.c,v
retrieving revision 1.82
diff -u -r1.82 usbdi.c
--- usbdi.c	24 Aug 2003 17:55:55 -0000	1.82
+++ usbdi.c	21 Sep 2003 15:28:29 -0000
@@ -751,6 +751,7 @@
 			    pipe, xfer, pipe->methods));
 		/* Make the HC abort it (and invoke the callback). */
 		pipe->methods->abort(xfer);
+		KASSERT(SIMPLEQ_FIRST(&pipe->queue) != xfer, ("usbd_ar_pipe"));
 		/* XXX only for non-0 usbd_clear_endpoint_stall(pipe); */
 	}
 	pipe->aborting = 0;
@@ -763,8 +764,9 @@
 {
 	usbd_pipe_handle pipe = xfer->pipe;
 	usb_dma_t *dmap = &xfer->dmabuf;
+	usbd_status status;
 	int repeat = pipe->repeat;
-	int polling;
+	int polling, xfer_flags;
 
 	SPLUSBCHECK;
 
@@ -835,30 +837,33 @@
 		xfer->status = USBD_SHORT_XFER;
 	}
 
-	if (xfer->callback)
-		xfer->callback(xfer, xfer->priv, xfer->status);
-
-#ifdef DIAGNOSTIC
-	if (pipe->methods->done != NULL)
+	/* Copy any xfer fields in case the xfer goes away in the callback. */
+	status = xfer->status;
+	xfer_flags = xfer->flags;
+	/*
+	 * For repeat operations, call the callback first, as the xfer
+	 * will not go away and the "done" method may modify it. Otherwise
+	 * reverse the order in case the callback wants to free or reuse
+	 * the xfer.
+	 */
+	if (repeat) {
+		if (xfer->callback)
+			xfer->callback(xfer, xfer->priv, status);
 		pipe->methods->done(xfer);
-	else
-		printf("usb_transfer_complete: pipe->methods->done == NULL\n");
-#else
-	pipe->methods->done(xfer);
-#endif
-
-	if ((xfer->flags & USBD_SYNCHRONOUS) && !polling)
-		wakeup(xfer);
+	} else {
+		pipe->methods->done(xfer);
+		if (xfer->callback)
+			xfer->callback(xfer, xfer->priv, status);
 
-	if (!repeat) {
 		/* XXX should we stop the queue on all errors? */
-		if ((xfer->status == USBD_CANCELLED ||
-		     xfer->status == USBD_TIMEOUT) &&
+		if ((status == USBD_CANCELLED || status == USBD_TIMEOUT) &&
 		    pipe->iface != NULL)		/* not control pipe */
 			pipe->running = 0;
 		else
 			usbd_start_next(pipe);
 	}
+	if ((xfer_flags & USBD_SYNCHRONOUS) && !polling)
+		wakeup(xfer);
 }
 
 usbd_status
@@ -879,6 +884,7 @@
 	xfer->busy_free = XFER_ONQU;
 #endif
 	s = splusb();
+	KASSERT(SIMPLEQ_FIRST(&pipe->queue) != xfer, ("usb_insert_transfer"));
 	SIMPLEQ_INSERT_TAIL(&pipe->queue, xfer, next);
 	if (pipe->running)
 		err = USBD_IN_PROGRESS;
Index: usbdi_util.c
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/usbdi_util.c,v
retrieving revision 1.31
diff -u -r1.31 usbdi_util.c
--- usbdi_util.c	24 Aug 2003 17:55:55 -0000	1.31
+++ usbdi_util.c	21 Sep 2003 15:28:30 -0000
@@ -440,7 +440,7 @@
 	}
 	usbd_get_xfer_status(xfer, NULL, NULL, size, &err);
 	DPRINTFN(1,("usbd_bulk_transfer: transferred %d\n", *size));
-	if (err) {
+	if (err == USBD_STALLED) {
 		DPRINTF(("usbd_bulk_transfer: error=%d\n", err));
 		usbd_clear_endpoint_stall(pipe);
 	}


More information about the freebsd-current mailing list