Date: Tue, 28 Nov 2006 14:53:26 +0100 From: "Daan Vreeken [PA4DAN]" <Danovitsch@vitsch.net> To: Ian Dowse <iedowse@iedowse.com> Cc: scottl@samsco.org, src-committers@freebsd.org, cvs-src@freebsd.org, cvs-all@freebsd.org, marius@alchemy.franken.de, "M. Warner Losh" <imp@bsdimp.com> Subject: Re: cvs commit: src/sys/dev/usb usbdi.c Message-ID: <200611281453.26820.Danovitsch@vitsch.net> In-Reply-To: <200611280939.aa56740@nowhere.iedowse.com> References: <200611280939.aa56740@nowhere.iedowse.com>
next in thread | previous in thread | raw e-mail | index | archive | help
On Tuesday 28 November 2006 10:39, Ian Dowse wrote: > In message <20061128.000354.1653105648.imp@bsdimp.com>, "M. Warner Losh" > writes > > >In message: <456BD0ED.4050305@samsco.org> > > > > Scott Long <scottl@samsco.org> writes: > >: Marius Strobl wrote: > >: > On Mon, Nov 27, 2006 at 10:32:12PM +0000, Ian Dowse wrote: > >: >> Thanks for spotting the typo - note though that the recently added > >: >> bus_dmamap_sync() call appears to be using the wrong bus_dma tag > >: >> and a potentially uninitialised map, so it is likely to only work > >: >> on architectures where bus_dmamap_sync doesn't depend on the tag > >: >> and map. > > > >That's weird, because it works on arm which does depend on it. > > There is a cpu_drain_writebuf() call in _bus_dmamap_sync() that > might be doing the trick on arm (assuming I'm looking at the right > file). The other operations there appear to rely on being passed > the correct map. > > >The original problem that motivated the change was that there was data > >that was returned on some transfers. This data wasn't sync, which > >caused us to get weird results when enumerating the device's data > >structures. The flush was needed because we don't map the data > >transfers as coherent (I believe, I've not studied it in a while, and > >my memory may be failing me. > > The SETUP packets that were mentioned in relation to the change are > allocated using usb_block_allocmem(), which does specify BUS_DMA_COHERENT > as far as I can tell (the contents of xfer->request are memcpy'd > into the reqdma buffer and it is then used). The other bus_dmamap_sync() > calls in usbdi.c all related to buffers passed in from outside USB > code, so the bus_dmamap_sync() calls are needed. I am the one who came up with the patch. Before I go into detail, let me say that I'm not a bus-dma guru (yet ;-), so what I did might be completely wrong or inappropriate. Last week I have started debugging the USB problems that I was seeing on the ARM board (KB9202B) that I have here. The board has an OHCI controller that is recognised by sys/arm/at91/ohci_atmelarm.c . The version that is currently in CVS doesn't work at al and crashes. After an initial patch to the ARM ohci driver I got the board to detect devices and data started moving around. ( http://docs.freebsd.org/cgi/getmsg.cgi?fetch=11034+0+archive/2006/freebsd-arm/20061126.freebsd-arm ) But there still where some problems with some devices. The first test I did seemed to have worked "by accident". Devices seemed to fail at random places and times. ( http://docs.freebsd.org/cgi/getmsg.cgi?fetch=31144+0+archive/2006/freebsd-arm/20061126.freebsd-arm ) Sometimes a device would get detected, sometimes it wouldn't. I even saw a simple "SET_ADDRESS" command fail once. Since SET_ADDRESS only consists of a SETUP packet and no data (in or out) I started to suspect the content of the SETUP packets (the content of xfer->request). Tracing the function calls from usbd_set_address() leas me to usbd_start_transfer(). In this function the data portion of a transfer is bus_dmamap_sync'd, but the request portion isn't. This made me believe that it might be needed to also sync the request portion there. Adding the call to bus_dmamap_sync() there as I did solved all the problems I was seeing and with it USB (on ARM at least) works like a charm and without problems. > >: BUS_DMA_COHERENT is meant to be a hint in terms of cross-platform > >: portability. A platform that supports it is supposed to then know > >: to short-circuit the sync calls if they aren't needed. > > > >COHERENT is still expensive to implement on some systems as it > >sometimes get mapped to uncached. This is OK for small data > >structures that the usb stack likes to push around internally, but > >isn't suitable for data that's pushed over the endpoints. > > > >NetBSD also offers a BUS_DMA_NOCACHE flag as well. I don't know how > >useful it is or where. > > If the arm platform requires bus_dmamap_sync() calls to flush out > writes even with BUS_DMA_COHERENT allocations, then that would > explain the original problem. We'll need to add bus_dmamap_sync() > calls in a number of places in each host controller driver where > there are data ordering requirements. Can someone confirm that the > arm platform needs these for correct write ordering? > > BTW, it looks like at the time the newly added bus_dmamap_sync() > call is executed, the SETUP packet data hasn't yet been copied into > the BUS_DMA_COHERENT buffer it will eventually use (that happens > in the pipe->methods->transfer() call afterwards). Is there more > information such as USB_DEBUG traces showing the original problem? Ah, you're right, it's called too early. I didn't dig deep enough into the code. To spare others the same route, I have written down the path through the code that's taken when calling usbd_set_address() : usbd_set_address() in usbdi_util.c usbd_do_request() in usbdi.c usbd_do_request_flags() in usbdi.c usbd_do_request_flags_pipe() in usbdi.c usbd_alloc_xfer() usbd_setup_default_xfer() in usbdi.c fills in xfer->* (especially it does "xfer->request = *req;" and "xfer->buffer=buffer;") usbd_sync_transfer() in usbdi.c usbd_transfer() in usbdi.c usbd_start_transfer() in usbdi.c if (have segs) && (isread) bus_dmamap_sync(PREWRITE) // my patch adds if (have a request) bus_dmamap_sync(PREWRITE) here pipe->methods->transfer() -> ohci_device_ctrl_transfer() in ohci.c usb_insert_transfer() in usbdi.c STAILQ_INSERT_TAIL(pipe->queue, xfer) ohci_device_ctrl_start() in ohci.c ohci_device_request() in ohci.c .. // only here the actual request data is copied... memcpy(KERNADDR(&opipe->u.ctl.reqdma), req, sizeof(*req)) .. My patch adds the sync call in usbd_start_transfer(), while the actual request data is copied into the right buffer later on in ohci_device_request(). For some reason the patch does work. If I disable this one-liner, all devices fail (most of the time). Re-enabling the line get the devices to work again. I have found one device that seems to fail consistently at the same spot (a USB HUB). With USB_DEBUG enabled I get the following dmesg output : --- log --- uhub_intr: sc=0xc0756950 usb_needs_explore usb_event_thread: woke up usb_discover uhub_explore: uhub0 port 1 status 0x0101 0x0001 uhub_explore: status change hub=1 port=1 uhub_intr: sc=0xc0756950 usb_needs_explore usbd_reset_port: port 1 reset done, error=NORMAL_COMPLETION uhub_intr: sc=0xc0756950 usb_needs_explore usbd_new_device bus=0xc0721000 port=1 depth=1 speed=2 usbd_setup_pipe: dev=0xc0818a80 iface=0 ep=0xc0818aa4 pipe=0xc0818a84 usbd_ar_pipe: pipe=0xc0818b80 usbd_setup_pipe: dev=0xc0818a80 iface=0 ep=0xc0818aa4 pipe=0xc0818a84 usbd_get_desc: type=1, index=0, len=8 usbd_new_device: adding unit addr=2, rev=101, class=9, subclass=0, protocol=0, usbd_ar_pipe: pipe=0xc0771b80 usbd_setup_pipe: dev=0xc0818a80 iface=0 ep=0xc0818aa4 pipe=0xc0818a84 usbd_get_device_desc: usbd_get_desc: type=1, index=0, len=18 usbd_new_device: new dev (addr 2), dev=0xc0818a80, parent=0xc074de00 usbd_probe_and_attach: trying device specific drivers usbd_get_string_desc: expected 16, got 4 uhub_match, dd=0xc0818abc uhub_match, dd=0xc0818abc uhub1: <vendor 0x05e3 U, class 9/0, rev 1.01/0.12, addr 2> on uhub0 uhub_attach usbd_get_config_desc: confidx=0 usbd_get_desc: type=2, index=0, len=9 usbd_get_config_desc: confidx=0, bad desc len=116 type=136 uhub1: configuration failed, error=INVAL device_attach: uhub1 attach returned 6 --- /log --- It seems to fail trying to load a 16 byte descriptor. (Once again, this HUB works with the patch in place). I don't believe my patch is at the right place where it's placed now. Any ideas from someone with more bus_dma-foo and/or usb-foo are highly appreciated :) If more debugging information is needed, just ask. -- Daan
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200611281453.26820.Danovitsch>