Source-Changes-HG archive

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

[src/trunk]: src/sys/dev Allow USB memory allocation by multiple segments in ...



details:   https://anonhg.NetBSD.org/src/rev/8215eecb54ca
branches:  trunk
changeset: 783749:8215eecb54ca
user:      prlw1 <prlw1%NetBSD.org@localhost>
date:      Mon Jan 07 15:07:40 2013 +0000

description:
Allow USB memory allocation by multiple segments in scatter/gather lists
rather than in a single contiguous block which causes problems with
large USB video frames.
Based on a patch by Jeremy Morse in the thread
http://mail-index.netbsd.org/current-users/2011/01/26/msg015532.html
Tested by developing http://code.opencv.org/issues/2360
OK jmcneill@

diffstat:

 sys/dev/usb/ehci.c    |  88 ++++++++++++++++++++++-----------------------
 sys/dev/usb/usb_mem.c |  98 +++++++++++++++++++++++++++++++++++++++++++-------
 sys/dev/usb/usb_mem.h |  20 ++++++---
 sys/dev/video.c       |   7 ++-
 4 files changed, 144 insertions(+), 69 deletions(-)

diffs (truncated from 489 to 300 lines):

diff -r a6dc96ae05dd -r 8215eecb54ca sys/dev/usb/ehci.c
--- a/sys/dev/usb/ehci.c        Mon Jan 07 13:23:46 2013 +0000
+++ b/sys/dev/usb/ehci.c        Mon Jan 07 15:07:40 2013 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ehci.c,v 1.196 2013/01/05 23:34:16 christos Exp $ */
+/*     $NetBSD: ehci.c,v 1.197 2013/01/07 15:07:40 prlw1 Exp $ */
 
 /*
  * Copyright (c) 2004-2012 The NetBSD Foundation, Inc.
@@ -53,7 +53,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.196 2013/01/05 23:34:16 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.197 2013/01/07 15:07:40 prlw1 Exp $");
 
 #include "ohci.h"
 #include "uhci.h"
@@ -1336,12 +1336,18 @@
        struct ehci_softc *sc = bus->hci_private;
        usbd_status err;
 
-       err = usb_allocmem(&sc->sc_bus, size, 0, dma);
+       err = usb_allocmem_flags(&sc->sc_bus, size, 0, dma, USBMALLOC_MULTISEG);
+#ifdef EHCI_DEBUG
+       if (err)
+               printf("ehci_allocm: usb_allocmem_flags()= %s (%d)\n",
+                       usbd_errstr(err), err);
+#endif
        if (err == USBD_NOMEM)
                err = usb_reserve_allocm(&sc->sc_dma_reserve, dma, size);
 #ifdef EHCI_DEBUG
        if (err)
-               printf("ehci_allocm: usb_allocmem()=%d\n", err);
+               printf("ehci_allocm: usb_reserve_allocm()= %s (%d)\n",
+                       usbd_errstr(err), err);
 #endif
        return (err);
 }
@@ -2727,18 +2733,20 @@
                     ehci_soft_qtd_t **sp, ehci_soft_qtd_t **ep)
 {
        ehci_soft_qtd_t *next, *cur;
-       ehci_physaddr_t dataphys, dataphyspage, dataphyslastpage, nextphys;
+       ehci_physaddr_t nextphys;
        u_int32_t qtdstatus;
        int len, curlen, mps;
        int i, tog;
+       int pages, pageoffs;
+       bus_size_t curoffs;
+       vaddr_t va, va_offs;
        usb_dma_t *dma = &xfer->dmabuf;
        u_int16_t flags = xfer->flags;
+       paddr_t a;
 
        DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen));
 
        len = alen;
-       dataphys = DMAADDR(dma, 0);
-       dataphyslastpage = EHCI_PAGE(dataphys + len - 1);
        qtdstatus = EHCI_QTD_ACTIVE |
            EHCI_QTD_SET_PID(rd ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) |
            EHCI_QTD_SET_CERR(3)
@@ -2756,28 +2764,18 @@
 
        usb_syncmem(dma, 0, alen,
            rd ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+       curoffs = 0;
        for (;;) {
-               dataphyspage = EHCI_PAGE(dataphys);
                /* The EHCI hardware can handle at most 5 pages. */
-               if (dataphyslastpage - dataphyspage <
-                   EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE) {
+               va_offs = (vaddr_t)KERNADDR(dma, curoffs);
+               va_offs = EHCI_PAGE_OFFSET(va_offs);
+               if (len-curoffs < EHCI_QTD_NBUFFERS*EHCI_PAGE_SIZE - va_offs) {
                        /* we can handle it in this QTD */
-                       curlen = len;
+                       curlen = len - curoffs;
                } else {
                        /* must use multiple TDs, fill as much as possible. */
-                       curlen = EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE -
-                                EHCI_PAGE_OFFSET(dataphys);
-#ifdef DIAGNOSTIC
-                       if (curlen > len) {
-                               printf("ehci_alloc_sqtd_chain: curlen=0x%x "
-                                      "len=0x%x offs=0x%x\n", curlen, len,
-                                      EHCI_PAGE_OFFSET(dataphys));
-                               printf("lastpage=0x%x page=0x%x phys=0x%x\n",
-                                      dataphyslastpage, dataphyspage,
-                                      dataphys);
-                               curlen = len;
-                       }
-#endif
+                       curlen = EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE - va_offs;
+
                        /* the length must be a multiple of the max size */
                        curlen -= curlen % mps;
                        DPRINTFN(1,("ehci_alloc_sqtd_chain: multiple QTDs, "
@@ -2787,18 +2785,16 @@
                                panic("ehci_alloc_sqtd_chain: curlen == 0");
 #endif
                }
-               DPRINTFN(4,("ehci_alloc_sqtd_chain: dataphys=0x%08x "
-                           "dataphyslastpage=0x%08x len=%d curlen=%d\n",
-                           dataphys, dataphyslastpage,
-                           len, curlen));
-               len -= curlen;
+               DPRINTFN(4,("ehci_alloc_sqtd_chain: len=%d curlen=%d "
+                           "curoffs=%d\n", len, curlen, curoffs));
 
                /*
                 * Allocate another transfer if there's more data left,
                 * or if force last short transfer flag is set and we're
                 * allocating a multiple of the max packet size.
                 */
-               if (len != 0 ||
+
+               if (curoffs + curlen != len ||
                    ((curlen % mps) == 0 && !rd && curlen != 0 &&
                     (flags & USBD_FORCE_SHORT_XFER))) {
                        next = ehci_alloc_sqtd(sc);
@@ -2810,20 +2806,21 @@
                        nextphys = EHCI_NULL;
                }
 
-               for (i = 0; i * EHCI_PAGE_SIZE <
-                           curlen + EHCI_PAGE_OFFSET(dataphys); i++) {
-                       ehci_physaddr_t a = dataphys + i * EHCI_PAGE_SIZE;
-                       if (i != 0) /* use offset only in first buffer */
-                               a = EHCI_PAGE(a);
-                       if (i >= EHCI_QTD_NBUFFERS) {
-#ifdef DIAGNOSTIC
-                               printf("ehci_alloc_sqtd_chain: i=%d\n", i);
-#endif
-                               goto nomem;
-                       }
-                       cur->qtd.qtd_buffer[i] = htole32(a);
-                       cur->qtd.qtd_buffer_hi[i] = 0;
+               /* Find number of pages we'll be using, insert dma addresses */
+               pages = EHCI_PAGE(curlen + EHCI_PAGE_SIZE -1) >> 12;
+               KASSERT(pages <= EHCI_QTD_NBUFFERS);
+               pageoffs = EHCI_PAGE(curoffs);
+               for (i = 0; i < pages; i++) {
+                       a = DMAADDR(dma, pageoffs + i * EHCI_PAGE_SIZE);
+                       cur->qtd.qtd_buffer[i] = htole32(a & 0xFFFFF000);
+                       /* Cast up to avoid compiler warnings */
+                       cur->qtd.qtd_buffer_hi[i] = htole32((uint64_t)a >> 32);
                }
+
+               /* First buffer pointer requires a page offset to start at */
+               va = (vaddr_t)KERNADDR(dma, curoffs);
+               cur->qtd.qtd_buffer[0] |= htole32(EHCI_PAGE_OFFSET(va));
+
                cur->nextqtd = next;
                cur->qtd.qtd_next = cur->qtd.qtd_altnext = nextphys;
                cur->qtd.qtd_status =
@@ -2832,7 +2829,8 @@
                cur->len = curlen;
 
                DPRINTFN(10,("ehci_alloc_sqtd_chain: cbp=0x%08x end=0x%08x\n",
-                           dataphys, dataphys + curlen));
+                           curoffs, curoffs + curlen));
+
                /* adjust the toggle based on the number of packets in this
                   qtd */
                if (((curlen + mps - 1) / mps) & 1) {
@@ -2845,7 +2843,7 @@
                    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
                DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n"));
                if (len)
-                       dataphys += curlen;
+                       curoffs += curlen;
                cur = next;
        }
        cur->qtd.qtd_status |= htole32(EHCI_QTD_IOC);
diff -r a6dc96ae05dd -r 8215eecb54ca sys/dev/usb/usb_mem.c
--- a/sys/dev/usb/usb_mem.c     Mon Jan 07 13:23:46 2013 +0000
+++ b/sys/dev/usb/usb_mem.c     Mon Jan 07 15:07:40 2013 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: usb_mem.c,v 1.54 2013/01/05 23:34:20 christos Exp $    */
+/*     $NetBSD: usb_mem.c,v 1.55 2013/01/07 15:07:41 prlw1 Exp $       */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -38,12 +38,12 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: usb_mem.c,v 1.54 2013/01/05 23:34:20 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: usb_mem.c,v 1.55 2013/01/07 15:07:41 prlw1 Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
-#include <sys/malloc.h>
+#include <sys/kmem.h>
 #include <sys/queue.h>
 #include <sys/device.h>                /* for usbdivar.h */
 #include <sys/bus.h>
@@ -82,7 +82,7 @@
 };
 
 Static usbd_status     usb_block_allocmem(bus_dma_tag_t, size_t, size_t,
-                                          usb_dma_block_t **);
+                                          usb_dma_block_t **, bool);
 Static void            usb_block_freemem(usb_dma_block_t *);
 
 LIST_HEAD(usb_dma_block_qh, usb_dma_block);
@@ -113,13 +113,20 @@
 
 Static usbd_status
 usb_block_allocmem(bus_dma_tag_t tag, size_t size, size_t align,
-                  usb_dma_block_t **dmap)
+                  usb_dma_block_t **dmap, bool multiseg)
 {
        usb_dma_block_t *b;
        int error;
 
        DPRINTFN(5, ("usb_block_allocmem: size=%zu align=%zu\n", size, align));
 
+       if (size == 0) {
+#ifdef DIAGNOSTIC
+               printf("usb_block_allocmem: called with size==0\n");
+#endif
+               return USBD_INVAL;
+       }
+
 #ifdef DIAGNOSTIC
        if (cpu_intr_p()) {
                printf("usb_block_allocmem: in interrupt context, size=%lu\n",
@@ -131,6 +138,9 @@
 
        /* First check the free list. */
        LIST_FOREACH(b, &usb_blk_freelist, next) {
+               /* Don't allocate multiple segments to unwilling callers */
+               if (b->nsegs != 1 && !multiseg)
+                       continue;
                if (b->tag == tag && b->size >= size && b->align >= align) {
                        LIST_REMOVE(b, next);
                        usb_blk_nfree--;
@@ -149,15 +159,27 @@
 #endif
 
        DPRINTFN(6, ("usb_block_allocmem: no free\n"));
-       b = malloc(sizeof *b, M_USB, M_NOWAIT | M_ZERO);
+       b = kmem_zalloc(sizeof *b, KM_SLEEP);
        if (b == NULL)
                return (USBD_NOMEM);
 
        b->tag = tag;
        b->size = size;
        b->align = align;
+
+       b->nsegs = (size + (PAGE_SIZE-1)) / PAGE_SIZE;
+       if (!multiseg)
+               /* Caller wants one segment */
+               b->nsegs = 1;
+
+       b->segs = kmem_alloc(b->nsegs * sizeof(*b->segs), KM_SLEEP);
+       if (b->segs == NULL) {
+               kmem_free(b, sizeof *b);
+               return USBD_NOMEM;
+       }
+
        error = bus_dmamem_alloc(tag, b->size, align, 0,
-                                b->segs, __arraycount(b->segs),
+                                b->segs, b->nsegs,
                                 &b->nsegs, BUS_DMA_NOWAIT);
        if (error)
                goto free0;
@@ -167,7 +189,7 @@
        if (error)
                goto free1;
 
-       error = bus_dmamap_create(tag, b->size, 1, b->size,
+       error = bus_dmamap_create(tag, b->size, b->nsegs, b->size,
                                  0, BUS_DMA_NOWAIT, &b->map);
        if (error)
                goto unmap;
@@ -181,6 +203,7 @@
 #ifdef USB_FRAG_DMA_WORKAROUND
        memset(b->kaddr, 0, b->size);
 #endif
+
        return (USBD_NORMAL_COMPLETION);
 
  destroy:
@@ -190,7 +213,8 @@
  free1:
        bus_dmamem_free(tag, b->segs, b->nsegs);
  free0:
-       free(b, M_USB);
+       kmem_free(b->segs, b->nsegs * sizeof(*b->segs));
+       kmem_free(b, sizeof *b);
        return (USBD_NOMEM);
 }
 
@@ -208,7 +232,8 @@
        bus_dmamap_destroy(b->tag, b->map);



Home | Main Index | Thread Index | Old Index