The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/x86/x86/busdma_bounce.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 1997, 1998 Justin T. Gibbs.
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions, and the following disclaimer,
   10  *    without modification, immediately at the beginning of the file.
   11  * 2. The name of the author may not be used to endorse or promote products
   12  *    derived from this software without specific prior written permission.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
   18  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD: releng/10.2/sys/x86/x86/busdma_bounce.c 282506 2015-05-05 19:47:17Z hselasky $");
   29 
   30 #include <sys/param.h>
   31 #include <sys/systm.h>
   32 #include <sys/malloc.h>
   33 #include <sys/bus.h>
   34 #include <sys/interrupt.h>
   35 #include <sys/kernel.h>
   36 #include <sys/ktr.h>
   37 #include <sys/lock.h>
   38 #include <sys/proc.h>
   39 #include <sys/memdesc.h>
   40 #include <sys/mutex.h>
   41 #include <sys/sysctl.h>
   42 #include <sys/uio.h>
   43 
   44 #include <vm/vm.h>
   45 #include <vm/vm_extern.h>
   46 #include <vm/vm_kern.h>
   47 #include <vm/vm_page.h>
   48 #include <vm/vm_map.h>
   49 
   50 #include <machine/atomic.h>
   51 #include <machine/bus.h>
   52 #include <machine/md_var.h>
   53 #include <machine/specialreg.h>
   54 #include <x86/include/busdma_impl.h>
   55 
   56 #ifdef __i386__
   57 #define MAX_BPAGES 512
   58 #else
   59 #define MAX_BPAGES 8192
   60 #endif
   61 
   62 enum {
   63         BUS_DMA_COULD_BOUNCE    = 0x01,
   64         BUS_DMA_MIN_ALLOC_COMP  = 0x02,
   65         BUS_DMA_KMEM_ALLOC      = 0x04,
   66 };
   67 
   68 struct bounce_zone;
   69 
   70 struct bus_dma_tag {
   71         struct bus_dma_tag_common common;
   72         int                     map_count;
   73         int                     bounce_flags;
   74         bus_dma_segment_t       *segments;
   75         struct bounce_zone      *bounce_zone;
   76 };
   77 
   78 struct bounce_page {
   79         vm_offset_t     vaddr;          /* kva of bounce buffer */
   80         bus_addr_t      busaddr;        /* Physical address */
   81         vm_offset_t     datavaddr;      /* kva of client data */
   82         bus_addr_t      dataaddr;       /* client physical address */
   83         bus_size_t      datacount;      /* client data count */
   84         STAILQ_ENTRY(bounce_page) links;
   85 };
   86 
   87 int busdma_swi_pending;
   88 
   89 struct bounce_zone {
   90         STAILQ_ENTRY(bounce_zone) links;
   91         STAILQ_HEAD(bp_list, bounce_page) bounce_page_list;
   92         int             total_bpages;
   93         int             free_bpages;
   94         int             reserved_bpages;
   95         int             active_bpages;
   96         int             total_bounced;
   97         int             total_deferred;
   98         int             map_count;
   99         bus_size_t      alignment;
  100         bus_addr_t      lowaddr;
  101         char            zoneid[8];
  102         char            lowaddrid[20];
  103         struct sysctl_ctx_list sysctl_tree;
  104         struct sysctl_oid *sysctl_tree_top;
  105 };
  106 
  107 static struct mtx bounce_lock;
  108 static int total_bpages;
  109 static int busdma_zonecount;
  110 static STAILQ_HEAD(, bounce_zone) bounce_zone_list;
  111 
  112 static SYSCTL_NODE(_hw, OID_AUTO, busdma, CTLFLAG_RD, 0, "Busdma parameters");
  113 SYSCTL_INT(_hw_busdma, OID_AUTO, total_bpages, CTLFLAG_RD, &total_bpages, 0,
  114            "Total bounce pages");
  115 
  116 struct bus_dmamap {
  117         struct bp_list         bpages;
  118         int                    pagesneeded;
  119         int                    pagesreserved;
  120         bus_dma_tag_t          dmat;
  121         struct memdesc         mem;
  122         bus_dmamap_callback_t *callback;
  123         void                  *callback_arg;
  124         STAILQ_ENTRY(bus_dmamap) links;
  125 };
  126 
  127 static STAILQ_HEAD(, bus_dmamap) bounce_map_waitinglist;
  128 static STAILQ_HEAD(, bus_dmamap) bounce_map_callbacklist;
  129 static struct bus_dmamap nobounce_dmamap;
  130 
  131 static void init_bounce_pages(void *dummy);
  132 static int alloc_bounce_zone(bus_dma_tag_t dmat);
  133 static int alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages);
  134 static int reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
  135                                 int commit);
  136 static bus_addr_t add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map,
  137                                   vm_offset_t vaddr, bus_addr_t addr,
  138                                   bus_size_t size);
  139 static void free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage);
  140 int run_filter(bus_dma_tag_t dmat, bus_addr_t paddr);
  141 static void _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
  142                                     pmap_t pmap, void *buf, bus_size_t buflen,
  143                                     int flags);
  144 static void _bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map,
  145                                    vm_paddr_t buf, bus_size_t buflen,
  146                                    int flags);
  147 static int _bus_dmamap_reserve_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
  148                                      int flags);
  149 
  150 #ifdef XEN
  151 #undef pmap_kextract
  152 #define pmap_kextract pmap_kextract_ma
  153 #endif
  154 
  155 /*
  156  * Allocate a device specific dma_tag.
  157  */
  158 static int
  159 bounce_bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
  160     bus_addr_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr,
  161     bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize,
  162     int nsegments, bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
  163     void *lockfuncarg, bus_dma_tag_t *dmat)
  164 {
  165         bus_dma_tag_t newtag;
  166         int error;
  167 
  168         *dmat = NULL;
  169         error = common_bus_dma_tag_create(parent != NULL ? &parent->common :
  170             NULL, alignment, boundary, lowaddr, highaddr, filter, filterarg,
  171             maxsize, nsegments, maxsegsz, flags, lockfunc, lockfuncarg,
  172             sizeof (struct bus_dma_tag), (void **)&newtag);
  173         if (error != 0)
  174                 return (error);
  175 
  176         newtag->common.impl = &bus_dma_bounce_impl;
  177         newtag->map_count = 0;
  178         newtag->segments = NULL;
  179 
  180         if (parent != NULL && ((newtag->common.filter != NULL) ||
  181             ((parent->bounce_flags & BUS_DMA_COULD_BOUNCE) != 0)))
  182                 newtag->bounce_flags |= BUS_DMA_COULD_BOUNCE;
  183 
  184         if (newtag->common.lowaddr < ptoa((vm_paddr_t)Maxmem) ||
  185             newtag->common.alignment > 1)
  186                 newtag->bounce_flags |= BUS_DMA_COULD_BOUNCE;
  187 
  188         if (((newtag->bounce_flags & BUS_DMA_COULD_BOUNCE) != 0) &&
  189             (flags & BUS_DMA_ALLOCNOW) != 0) {
  190                 struct bounce_zone *bz;
  191 
  192                 /* Must bounce */
  193                 if ((error = alloc_bounce_zone(newtag)) != 0) {
  194                         free(newtag, M_DEVBUF);
  195                         return (error);
  196                 }
  197                 bz = newtag->bounce_zone;
  198 
  199                 if (ptoa(bz->total_bpages) < maxsize) {
  200                         int pages;
  201 
  202                         pages = atop(maxsize) - bz->total_bpages;
  203 
  204                         /* Add pages to our bounce pool */
  205                         if (alloc_bounce_pages(newtag, pages) < pages)
  206                                 error = ENOMEM;
  207                 }
  208                 /* Performed initial allocation */
  209                 newtag->bounce_flags |= BUS_DMA_MIN_ALLOC_COMP;
  210         } else
  211                 error = 0;
  212         
  213         if (error != 0)
  214                 free(newtag, M_DEVBUF);
  215         else
  216                 *dmat = newtag;
  217         CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d",
  218             __func__, newtag, (newtag != NULL ? newtag->common.flags : 0),
  219             error);
  220         return (error);
  221 }
  222 
  223 static int
  224 bounce_bus_dma_tag_destroy(bus_dma_tag_t dmat)
  225 {
  226         bus_dma_tag_t dmat_copy, parent;
  227         int error;
  228 
  229         error = 0;
  230         dmat_copy = dmat;
  231 
  232         if (dmat != NULL) {
  233                 if (dmat->map_count != 0) {
  234                         error = EBUSY;
  235                         goto out;
  236                 }
  237                 while (dmat != NULL) {
  238                         parent = (bus_dma_tag_t)dmat->common.parent;
  239                         atomic_subtract_int(&dmat->common.ref_count, 1);
  240                         if (dmat->common.ref_count == 0) {
  241                                 if (dmat->segments != NULL)
  242                                         free(dmat->segments, M_DEVBUF);
  243                                 free(dmat, M_DEVBUF);
  244                                 /*
  245                                  * Last reference count, so
  246                                  * release our reference
  247                                  * count on our parent.
  248                                  */
  249                                 dmat = parent;
  250                         } else
  251                                 dmat = NULL;
  252                 }
  253         }
  254 out:
  255         CTR3(KTR_BUSDMA, "%s tag %p error %d", __func__, dmat_copy, error);
  256         return (error);
  257 }
  258 
  259 /*
  260  * Allocate a handle for mapping from kva/uva/physical
  261  * address space into bus device space.
  262  */
  263 static int
  264 bounce_bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)
  265 {
  266         struct bounce_zone *bz;
  267         int error, maxpages, pages;
  268 
  269         error = 0;
  270 
  271         if (dmat->segments == NULL) {
  272                 dmat->segments = (bus_dma_segment_t *)malloc(
  273                     sizeof(bus_dma_segment_t) * dmat->common.nsegments,
  274                     M_DEVBUF, M_NOWAIT);
  275                 if (dmat->segments == NULL) {
  276                         CTR3(KTR_BUSDMA, "%s: tag %p error %d",
  277                             __func__, dmat, ENOMEM);
  278                         return (ENOMEM);
  279                 }
  280         }
  281 
  282         /*
  283          * Bouncing might be required if the driver asks for an active
  284          * exclusion region, a data alignment that is stricter than 1, and/or
  285          * an active address boundary.
  286          */
  287         if (dmat->bounce_flags & BUS_DMA_COULD_BOUNCE) {
  288                 /* Must bounce */
  289                 if (dmat->bounce_zone == NULL) {
  290                         if ((error = alloc_bounce_zone(dmat)) != 0)
  291                                 return (error);
  292                 }
  293                 bz = dmat->bounce_zone;
  294 
  295                 *mapp = (bus_dmamap_t)malloc(sizeof(**mapp), M_DEVBUF,
  296                     M_NOWAIT | M_ZERO);
  297                 if (*mapp == NULL) {
  298                         CTR3(KTR_BUSDMA, "%s: tag %p error %d",
  299                             __func__, dmat, ENOMEM);
  300                         return (ENOMEM);
  301                 }
  302 
  303                 /* Initialize the new map */
  304                 STAILQ_INIT(&((*mapp)->bpages));
  305 
  306                 /*
  307                  * Attempt to add pages to our pool on a per-instance
  308                  * basis up to a sane limit.
  309                  */
  310                 if (dmat->common.alignment > 1)
  311                         maxpages = MAX_BPAGES;
  312                 else
  313                         maxpages = MIN(MAX_BPAGES, Maxmem -
  314                             atop(dmat->common.lowaddr));
  315                 if ((dmat->bounce_flags & BUS_DMA_MIN_ALLOC_COMP) == 0 ||
  316                     (bz->map_count > 0 && bz->total_bpages < maxpages)) {
  317                         pages = MAX(atop(dmat->common.maxsize), 1);
  318                         pages = MIN(maxpages - bz->total_bpages, pages);
  319                         pages = MAX(pages, 1);
  320                         if (alloc_bounce_pages(dmat, pages) < pages)
  321                                 error = ENOMEM;
  322                         if ((dmat->bounce_flags & BUS_DMA_MIN_ALLOC_COMP)
  323                             == 0) {
  324                                 if (error == 0) {
  325                                         dmat->bounce_flags |=
  326                                             BUS_DMA_MIN_ALLOC_COMP;
  327                                 }
  328                         } else
  329                                 error = 0;
  330                 }
  331                 bz->map_count++;
  332         } else {
  333                 *mapp = NULL;
  334         }
  335         if (error == 0)
  336                 dmat->map_count++;
  337         CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  338             __func__, dmat, dmat->common.flags, error);
  339         return (error);
  340 }
  341 
  342 /*
  343  * Destroy a handle for mapping from kva/uva/physical
  344  * address space into bus device space.
  345  */
  346 static int
  347 bounce_bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map)
  348 {
  349 
  350         if (map != NULL && map != &nobounce_dmamap) {
  351                 if (STAILQ_FIRST(&map->bpages) != NULL) {
  352                         CTR3(KTR_BUSDMA, "%s: tag %p error %d",
  353                             __func__, dmat, EBUSY);
  354                         return (EBUSY);
  355                 }
  356                 if (dmat->bounce_zone)
  357                         dmat->bounce_zone->map_count--;
  358                 free(map, M_DEVBUF);
  359         }
  360         dmat->map_count--;
  361         CTR2(KTR_BUSDMA, "%s: tag %p error 0", __func__, dmat);
  362         return (0);
  363 }
  364 
  365 
  366 /*
  367  * Allocate a piece of memory that can be efficiently mapped into
  368  * bus device space based on the constraints lited in the dma tag.
  369  * A dmamap to for use with dmamap_load is also allocated.
  370  */
  371 static int
  372 bounce_bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
  373     bus_dmamap_t *mapp)
  374 {
  375         vm_memattr_t attr;
  376         int mflags;
  377 
  378         if (flags & BUS_DMA_NOWAIT)
  379                 mflags = M_NOWAIT;
  380         else
  381                 mflags = M_WAITOK;
  382 
  383         /* If we succeed, no mapping/bouncing will be required */
  384         *mapp = NULL;
  385 
  386         if (dmat->segments == NULL) {
  387                 dmat->segments = (bus_dma_segment_t *)malloc(
  388                     sizeof(bus_dma_segment_t) * dmat->common.nsegments,
  389                     M_DEVBUF, mflags);
  390                 if (dmat->segments == NULL) {
  391                         CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  392                             __func__, dmat, dmat->common.flags, ENOMEM);
  393                         return (ENOMEM);
  394                 }
  395         }
  396         if (flags & BUS_DMA_ZERO)
  397                 mflags |= M_ZERO;
  398         if (flags & BUS_DMA_NOCACHE)
  399                 attr = VM_MEMATTR_UNCACHEABLE;
  400         else
  401                 attr = VM_MEMATTR_DEFAULT;
  402 
  403         /* 
  404          * XXX:
  405          * (dmat->alignment < dmat->maxsize) is just a quick hack; the exact
  406          * alignment guarantees of malloc need to be nailed down, and the
  407          * code below should be rewritten to take that into account.
  408          *
  409          * In the meantime, we'll warn the user if malloc gets it wrong.
  410          */
  411         if ((dmat->common.maxsize <= PAGE_SIZE) &&
  412            (dmat->common.alignment < dmat->common.maxsize) &&
  413             dmat->common.lowaddr >= ptoa((vm_paddr_t)Maxmem) &&
  414             attr == VM_MEMATTR_DEFAULT) {
  415                 *vaddr = malloc(dmat->common.maxsize, M_DEVBUF, mflags);
  416         } else if (dmat->common.nsegments >= btoc(dmat->common.maxsize) &&
  417             dmat->common.alignment <= PAGE_SIZE &&
  418             (dmat->common.boundary == 0 ||
  419             dmat->common.boundary >= dmat->common.lowaddr)) {
  420                 /* Page-based multi-segment allocations allowed */
  421                 *vaddr = (void *)kmem_alloc_attr(kernel_arena,
  422                     dmat->common.maxsize, mflags, 0ul, dmat->common.lowaddr,
  423                     attr);
  424                 dmat->bounce_flags |= BUS_DMA_KMEM_ALLOC;
  425         } else {
  426                 *vaddr = (void *)kmem_alloc_contig(kernel_arena,
  427                     dmat->common.maxsize, mflags, 0ul, dmat->common.lowaddr,
  428                     dmat->common.alignment != 0 ? dmat->common.alignment : 1ul,
  429                     dmat->common.boundary, attr);
  430                 dmat->bounce_flags |= BUS_DMA_KMEM_ALLOC;
  431         }
  432         if (*vaddr == NULL) {
  433                 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  434                     __func__, dmat, dmat->common.flags, ENOMEM);
  435                 return (ENOMEM);
  436         } else if (vtophys(*vaddr) & (dmat->common.alignment - 1)) {
  437                 printf("bus_dmamem_alloc failed to align memory properly.\n");
  438         }
  439         CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  440             __func__, dmat, dmat->common.flags, 0);
  441         return (0);
  442 }
  443 
  444 /*
  445  * Free a piece of memory and it's allociated dmamap, that was allocated
  446  * via bus_dmamem_alloc.  Make the same choice for free/contigfree.
  447  */
  448 static void
  449 bounce_bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)
  450 {
  451         /*
  452          * dmamem does not need to be bounced, so the map should be
  453          * NULL and the BUS_DMA_KMEM_ALLOC flag cleared if malloc()
  454          * was used and set if kmem_alloc_contig() was used.
  455          */
  456         if (map != NULL)
  457                 panic("bus_dmamem_free: Invalid map freed\n");
  458         if ((dmat->bounce_flags & BUS_DMA_KMEM_ALLOC) == 0)
  459                 free(vaddr, M_DEVBUF);
  460         else
  461                 kmem_free(kernel_arena, (vm_offset_t)vaddr,
  462                     dmat->common.maxsize);
  463         CTR3(KTR_BUSDMA, "%s: tag %p flags 0x%x", __func__, dmat,
  464             dmat->bounce_flags);
  465 }
  466 
  467 static void
  468 _bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map, vm_paddr_t buf,
  469     bus_size_t buflen, int flags)
  470 {
  471         bus_addr_t curaddr;
  472         bus_size_t sgsize;
  473 
  474         if ((map != &nobounce_dmamap && map->pagesneeded == 0)) {
  475                 /*
  476                  * Count the number of bounce pages
  477                  * needed in order to complete this transfer
  478                  */
  479                 curaddr = buf;
  480                 while (buflen != 0) {
  481                         sgsize = MIN(buflen, dmat->common.maxsegsz);
  482                         if (bus_dma_run_filter(&dmat->common, curaddr)) {
  483                                 sgsize = MIN(sgsize, PAGE_SIZE);
  484                                 map->pagesneeded++;
  485                         }
  486                         curaddr += sgsize;
  487                         buflen -= sgsize;
  488                 }
  489                 CTR1(KTR_BUSDMA, "pagesneeded= %d\n", map->pagesneeded);
  490         }
  491 }
  492 
  493 static void
  494 _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map, pmap_t pmap,
  495     void *buf, bus_size_t buflen, int flags)
  496 {
  497         vm_offset_t vaddr;
  498         vm_offset_t vendaddr;
  499         bus_addr_t paddr;
  500         bus_size_t sg_len;
  501 
  502         if ((map != &nobounce_dmamap && map->pagesneeded == 0)) {
  503                 CTR4(KTR_BUSDMA, "lowaddr= %d Maxmem= %d, boundary= %d, "
  504                     "alignment= %d", dmat->common.lowaddr,
  505                     ptoa((vm_paddr_t)Maxmem),
  506                     dmat->common.boundary, dmat->common.alignment);
  507                 CTR3(KTR_BUSDMA, "map= %p, nobouncemap= %p, pagesneeded= %d",
  508                     map, &nobounce_dmamap, map->pagesneeded);
  509                 /*
  510                  * Count the number of bounce pages
  511                  * needed in order to complete this transfer
  512                  */
  513                 vaddr = (vm_offset_t)buf;
  514                 vendaddr = (vm_offset_t)buf + buflen;
  515 
  516                 while (vaddr < vendaddr) {
  517                         sg_len = PAGE_SIZE - ((vm_offset_t)vaddr & PAGE_MASK);
  518                         if (pmap == kernel_pmap)
  519                                 paddr = pmap_kextract(vaddr);
  520                         else
  521                                 paddr = pmap_extract(pmap, vaddr);
  522                         if (bus_dma_run_filter(&dmat->common, paddr) != 0) {
  523                                 sg_len = roundup2(sg_len,
  524                                     dmat->common.alignment);
  525                                 map->pagesneeded++;
  526                         }
  527                         vaddr += sg_len;
  528                 }
  529                 CTR1(KTR_BUSDMA, "pagesneeded= %d\n", map->pagesneeded);
  530         }
  531 }
  532 
  533 static int
  534 _bus_dmamap_reserve_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int flags)
  535 {
  536 
  537         /* Reserve Necessary Bounce Pages */
  538         mtx_lock(&bounce_lock);
  539         if (flags & BUS_DMA_NOWAIT) {
  540                 if (reserve_bounce_pages(dmat, map, 0) != 0) {
  541                         mtx_unlock(&bounce_lock);
  542                         return (ENOMEM);
  543                 }
  544         } else {
  545                 if (reserve_bounce_pages(dmat, map, 1) != 0) {
  546                         /* Queue us for resources */
  547                         STAILQ_INSERT_TAIL(&bounce_map_waitinglist, map, links);
  548                         mtx_unlock(&bounce_lock);
  549                         return (EINPROGRESS);
  550                 }
  551         }
  552         mtx_unlock(&bounce_lock);
  553 
  554         return (0);
  555 }
  556 
  557 /*
  558  * Add a single contiguous physical range to the segment list.
  559  */
  560 static int
  561 _bus_dmamap_addseg(bus_dma_tag_t dmat, bus_dmamap_t map, bus_addr_t curaddr,
  562     bus_size_t sgsize, bus_dma_segment_t *segs, int *segp)
  563 {
  564         bus_addr_t baddr, bmask;
  565         int seg;
  566 
  567         /*
  568          * Make sure we don't cross any boundaries.
  569          */
  570         bmask = ~(dmat->common.boundary - 1);
  571         if (dmat->common.boundary > 0) {
  572                 baddr = (curaddr + dmat->common.boundary) & bmask;
  573                 if (sgsize > (baddr - curaddr))
  574                         sgsize = (baddr - curaddr);
  575         }
  576 
  577         /*
  578          * Insert chunk into a segment, coalescing with
  579          * previous segment if possible.
  580          */
  581         seg = *segp;
  582         if (seg == -1) {
  583                 seg = 0;
  584                 segs[seg].ds_addr = curaddr;
  585                 segs[seg].ds_len = sgsize;
  586         } else {
  587                 if (curaddr == segs[seg].ds_addr + segs[seg].ds_len &&
  588                     (segs[seg].ds_len + sgsize) <= dmat->common.maxsegsz &&
  589                     (dmat->common.boundary == 0 ||
  590                      (segs[seg].ds_addr & bmask) == (curaddr & bmask)))
  591                         segs[seg].ds_len += sgsize;
  592                 else {
  593                         if (++seg >= dmat->common.nsegments)
  594                                 return (0);
  595                         segs[seg].ds_addr = curaddr;
  596                         segs[seg].ds_len = sgsize;
  597                 }
  598         }
  599         *segp = seg;
  600         return (sgsize);
  601 }
  602 
  603 /*
  604  * Utility function to load a physical buffer.  segp contains
  605  * the starting segment on entrace, and the ending segment on exit.
  606  */
  607 static int
  608 bounce_bus_dmamap_load_phys(bus_dma_tag_t dmat, bus_dmamap_t map,
  609     vm_paddr_t buf, bus_size_t buflen, int flags, bus_dma_segment_t *segs,
  610     int *segp)
  611 {
  612         bus_size_t sgsize;
  613         bus_addr_t curaddr;
  614         int error;
  615 
  616         if (map == NULL)
  617                 map = &nobounce_dmamap;
  618 
  619         if (segs == NULL)
  620                 segs = dmat->segments;
  621 
  622         if ((dmat->bounce_flags & BUS_DMA_COULD_BOUNCE) != 0) {
  623                 _bus_dmamap_count_phys(dmat, map, buf, buflen, flags);
  624                 if (map->pagesneeded != 0) {
  625                         error = _bus_dmamap_reserve_pages(dmat, map, flags);
  626                         if (error)
  627                                 return (error);
  628                 }
  629         }
  630 
  631         while (buflen > 0) {
  632                 curaddr = buf;
  633                 sgsize = MIN(buflen, dmat->common.maxsegsz);
  634                 if (((dmat->bounce_flags & BUS_DMA_COULD_BOUNCE) != 0) &&
  635                     map->pagesneeded != 0 &&
  636                     bus_dma_run_filter(&dmat->common, curaddr)) {
  637                         sgsize = MIN(sgsize, PAGE_SIZE);
  638                         curaddr = add_bounce_page(dmat, map, 0, curaddr,
  639                             sgsize);
  640                 }
  641                 sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs,
  642                     segp);
  643                 if (sgsize == 0)
  644                         break;
  645                 buf += sgsize;
  646                 buflen -= sgsize;
  647         }
  648 
  649         /*
  650          * Did we fit?
  651          */
  652         return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */
  653 }
  654 
  655 /*
  656  * Utility function to load a linear buffer.  segp contains
  657  * the starting segment on entrace, and the ending segment on exit.
  658  */
  659 static int
  660 bounce_bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf,
  661     bus_size_t buflen, pmap_t pmap, int flags, bus_dma_segment_t *segs,
  662     int *segp)
  663 {
  664         bus_size_t sgsize, max_sgsize;
  665         bus_addr_t curaddr;
  666         vm_offset_t vaddr;
  667         int error;
  668 
  669         if (map == NULL)
  670                 map = &nobounce_dmamap;
  671 
  672         if (segs == NULL)
  673                 segs = dmat->segments;
  674 
  675         if ((dmat->bounce_flags & BUS_DMA_COULD_BOUNCE) != 0) {
  676                 _bus_dmamap_count_pages(dmat, map, pmap, buf, buflen, flags);
  677                 if (map->pagesneeded != 0) {
  678                         error = _bus_dmamap_reserve_pages(dmat, map, flags);
  679                         if (error)
  680                                 return (error);
  681                 }
  682         }
  683 
  684         vaddr = (vm_offset_t)buf;
  685         while (buflen > 0) {
  686                 /*
  687                  * Get the physical address for this segment.
  688                  */
  689                 if (pmap == kernel_pmap)
  690                         curaddr = pmap_kextract(vaddr);
  691                 else
  692                         curaddr = pmap_extract(pmap, vaddr);
  693 
  694                 /*
  695                  * Compute the segment size, and adjust counts.
  696                  */
  697                 max_sgsize = MIN(buflen, dmat->common.maxsegsz);
  698                 sgsize = PAGE_SIZE - ((vm_offset_t)curaddr & PAGE_MASK);
  699                 if (((dmat->bounce_flags & BUS_DMA_COULD_BOUNCE) != 0) &&
  700                     map->pagesneeded != 0 &&
  701                     bus_dma_run_filter(&dmat->common, curaddr)) {
  702                         sgsize = roundup2(sgsize, dmat->common.alignment);
  703                         sgsize = MIN(sgsize, max_sgsize);
  704                         curaddr = add_bounce_page(dmat, map, vaddr, curaddr,
  705                             sgsize);
  706                 } else {
  707                         sgsize = MIN(sgsize, max_sgsize);
  708                 }
  709                 sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs,
  710                     segp);
  711                 if (sgsize == 0)
  712                         break;
  713                 vaddr += sgsize;
  714                 buflen -= sgsize;
  715         }
  716 
  717         /*
  718          * Did we fit?
  719          */
  720         return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */
  721 }
  722 
  723 static void
  724 bounce_bus_dmamap_waitok(bus_dma_tag_t dmat, bus_dmamap_t map,
  725     struct memdesc *mem, bus_dmamap_callback_t *callback, void *callback_arg)
  726 {
  727 
  728         if (map == NULL)
  729                 return;
  730         map->mem = *mem;
  731         map->dmat = dmat;
  732         map->callback = callback;
  733         map->callback_arg = callback_arg;
  734 }
  735 
  736 static bus_dma_segment_t *
  737 bounce_bus_dmamap_complete(bus_dma_tag_t dmat, bus_dmamap_t map,
  738     bus_dma_segment_t *segs, int nsegs, int error)
  739 {
  740 
  741         if (segs == NULL)
  742                 segs = dmat->segments;
  743         return (segs);
  744 }
  745 
  746 /*
  747  * Release the mapping held by map.
  748  */
  749 static void
  750 bounce_bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
  751 {
  752         struct bounce_page *bpage;
  753 
  754         while ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
  755                 STAILQ_REMOVE_HEAD(&map->bpages, links);
  756                 free_bounce_page(dmat, bpage);
  757         }
  758 }
  759 
  760 static void
  761 bounce_bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map,
  762     bus_dmasync_op_t op)
  763 {
  764         struct bounce_page *bpage;
  765 
  766         if ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
  767                 /*
  768                  * Handle data bouncing.  We might also
  769                  * want to add support for invalidating
  770                  * the caches on broken hardware
  771                  */
  772                 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x op 0x%x "
  773                     "performing bounce", __func__, dmat,
  774                     dmat->common.flags, op);
  775 
  776                 if ((op & BUS_DMASYNC_PREWRITE) != 0) {
  777                         while (bpage != NULL) {
  778                                 if (bpage->datavaddr != 0) {
  779                                         bcopy((void *)bpage->datavaddr,
  780                                             (void *)bpage->vaddr,
  781                                             bpage->datacount);
  782                                 } else {
  783                                         physcopyout(bpage->dataaddr,
  784                                             (void *)bpage->vaddr,
  785                                             bpage->datacount);
  786                                 }
  787                                 bpage = STAILQ_NEXT(bpage, links);
  788                         }
  789                         dmat->bounce_zone->total_bounced++;
  790                 }
  791 
  792                 if ((op & BUS_DMASYNC_POSTREAD) != 0) {
  793                         while (bpage != NULL) {
  794                                 if (bpage->datavaddr != 0) {
  795                                         bcopy((void *)bpage->vaddr,
  796                                             (void *)bpage->datavaddr,
  797                                             bpage->datacount);
  798                                 } else {
  799                                         physcopyin((void *)bpage->vaddr,
  800                                             bpage->dataaddr,
  801                                             bpage->datacount);
  802                                 }
  803                                 bpage = STAILQ_NEXT(bpage, links);
  804                         }
  805                         dmat->bounce_zone->total_bounced++;
  806                 }
  807         }
  808 }
  809 
  810 static void
  811 init_bounce_pages(void *dummy __unused)
  812 {
  813 
  814         total_bpages = 0;
  815         STAILQ_INIT(&bounce_zone_list);
  816         STAILQ_INIT(&bounce_map_waitinglist);
  817         STAILQ_INIT(&bounce_map_callbacklist);
  818         mtx_init(&bounce_lock, "bounce pages lock", NULL, MTX_DEF);
  819 }
  820 SYSINIT(bpages, SI_SUB_LOCK, SI_ORDER_ANY, init_bounce_pages, NULL);
  821 
  822 static struct sysctl_ctx_list *
  823 busdma_sysctl_tree(struct bounce_zone *bz)
  824 {
  825         return (&bz->sysctl_tree);
  826 }
  827 
  828 static struct sysctl_oid *
  829 busdma_sysctl_tree_top(struct bounce_zone *bz)
  830 {
  831         return (bz->sysctl_tree_top);
  832 }
  833 
  834 static int
  835 alloc_bounce_zone(bus_dma_tag_t dmat)
  836 {
  837         struct bounce_zone *bz;
  838 
  839         /* Check to see if we already have a suitable zone */
  840         STAILQ_FOREACH(bz, &bounce_zone_list, links) {
  841                 if ((dmat->common.alignment <= bz->alignment) &&
  842                     (dmat->common.lowaddr >= bz->lowaddr)) {
  843                         dmat->bounce_zone = bz;
  844                         return (0);
  845                 }
  846         }
  847 
  848         if ((bz = (struct bounce_zone *)malloc(sizeof(*bz), M_DEVBUF,
  849             M_NOWAIT | M_ZERO)) == NULL)
  850                 return (ENOMEM);
  851 
  852         STAILQ_INIT(&bz->bounce_page_list);
  853         bz->free_bpages = 0;
  854         bz->reserved_bpages = 0;
  855         bz->active_bpages = 0;
  856         bz->lowaddr = dmat->common.lowaddr;
  857         bz->alignment = MAX(dmat->common.alignment, PAGE_SIZE);
  858         bz->map_count = 0;
  859         snprintf(bz->zoneid, 8, "zone%d", busdma_zonecount);
  860         busdma_zonecount++;
  861         snprintf(bz->lowaddrid, 18, "%#jx", (uintmax_t)bz->lowaddr);
  862         STAILQ_INSERT_TAIL(&bounce_zone_list, bz, links);
  863         dmat->bounce_zone = bz;
  864 
  865         sysctl_ctx_init(&bz->sysctl_tree);
  866         bz->sysctl_tree_top = SYSCTL_ADD_NODE(&bz->sysctl_tree,
  867             SYSCTL_STATIC_CHILDREN(_hw_busdma), OID_AUTO, bz->zoneid,
  868             CTLFLAG_RD, 0, "");
  869         if (bz->sysctl_tree_top == NULL) {
  870                 sysctl_ctx_free(&bz->sysctl_tree);
  871                 return (0);     /* XXX error code? */
  872         }
  873 
  874         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
  875             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
  876             "total_bpages", CTLFLAG_RD, &bz->total_bpages, 0,
  877             "Total bounce pages");
  878         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
  879             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
  880             "free_bpages", CTLFLAG_RD, &bz->free_bpages, 0,
  881             "Free bounce pages");
  882         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
  883             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
  884             "reserved_bpages", CTLFLAG_RD, &bz->reserved_bpages, 0,
  885             "Reserved bounce pages");
  886         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
  887             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
  888             "active_bpages", CTLFLAG_RD, &bz->active_bpages, 0,
  889             "Active bounce pages");
  890         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
  891             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
  892             "total_bounced", CTLFLAG_RD, &bz->total_bounced, 0,
  893             "Total bounce requests");
  894         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
  895             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
  896             "total_deferred", CTLFLAG_RD, &bz->total_deferred, 0,
  897             "Total bounce requests that were deferred");
  898         SYSCTL_ADD_STRING(busdma_sysctl_tree(bz),
  899             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
  900             "lowaddr", CTLFLAG_RD, bz->lowaddrid, 0, "");
  901         SYSCTL_ADD_UAUTO(busdma_sysctl_tree(bz),
  902             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
  903             "alignment", CTLFLAG_RD, &bz->alignment, "");
  904 
  905         return (0);
  906 }
  907 
  908 static int
  909 alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages)
  910 {
  911         struct bounce_zone *bz;
  912         int count;
  913 
  914         bz = dmat->bounce_zone;
  915         count = 0;
  916         while (numpages > 0) {
  917                 struct bounce_page *bpage;
  918 
  919                 bpage = (struct bounce_page *)malloc(sizeof(*bpage), M_DEVBUF,
  920                                                      M_NOWAIT | M_ZERO);
  921 
  922                 if (bpage == NULL)
  923                         break;
  924                 bpage->vaddr = (vm_offset_t)contigmalloc(PAGE_SIZE, M_DEVBUF,
  925                                                          M_NOWAIT, 0ul,
  926                                                          bz->lowaddr,
  927                                                          PAGE_SIZE,
  928                                                          0);
  929                 if (bpage->vaddr == 0) {
  930                         free(bpage, M_DEVBUF);
  931                         break;
  932                 }
  933                 bpage->busaddr = pmap_kextract(bpage->vaddr);
  934                 mtx_lock(&bounce_lock);
  935                 STAILQ_INSERT_TAIL(&bz->bounce_page_list, bpage, links);
  936                 total_bpages++;
  937                 bz->total_bpages++;
  938                 bz->free_bpages++;
  939                 mtx_unlock(&bounce_lock);
  940                 count++;
  941                 numpages--;
  942         }
  943         return (count);
  944 }
  945 
  946 static int
  947 reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int commit)
  948 {
  949         struct bounce_zone *bz;
  950         int pages;
  951 
  952         mtx_assert(&bounce_lock, MA_OWNED);
  953         bz = dmat->bounce_zone;
  954         pages = MIN(bz->free_bpages, map->pagesneeded - map->pagesreserved);
  955         if (commit == 0 && map->pagesneeded > (map->pagesreserved + pages))
  956                 return (map->pagesneeded - (map->pagesreserved + pages));
  957         bz->free_bpages -= pages;
  958         bz->reserved_bpages += pages;
  959         map->pagesreserved += pages;
  960         pages = map->pagesneeded - map->pagesreserved;
  961 
  962         return (pages);
  963 }
  964 
  965 static bus_addr_t
  966 add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map, vm_offset_t vaddr,
  967                 bus_addr_t addr, bus_size_t size)
  968 {
  969         struct bounce_zone *bz;
  970         struct bounce_page *bpage;
  971 
  972         KASSERT(dmat->bounce_zone != NULL, ("no bounce zone in dma tag"));
  973         KASSERT(map != NULL && map != &nobounce_dmamap,
  974             ("add_bounce_page: bad map %p", map));
  975 
  976         bz = dmat->bounce_zone;
  977         if (map->pagesneeded == 0)
  978                 panic("add_bounce_page: map doesn't need any pages");
  979         map->pagesneeded--;
  980 
  981         if (map->pagesreserved == 0)
  982                 panic("add_bounce_page: map doesn't need any pages");
  983         map->pagesreserved--;
  984 
  985         mtx_lock(&bounce_lock);
  986         bpage = STAILQ_FIRST(&bz->bounce_page_list);
  987         if (bpage == NULL)
  988                 panic("add_bounce_page: free page list is empty");
  989 
  990         STAILQ_REMOVE_HEAD(&bz->bounce_page_list, links);
  991         bz->reserved_bpages--;
  992         bz->active_bpages++;
  993         mtx_unlock(&bounce_lock);
  994 
  995         if (dmat->common.flags & BUS_DMA_KEEP_PG_OFFSET) {
  996                 /* Page offset needs to be preserved. */
  997                 bpage->vaddr |= addr & PAGE_MASK;
  998                 bpage->busaddr |= addr & PAGE_MASK;
  999         }
 1000         bpage->datavaddr = vaddr;
 1001         bpage->dataaddr = addr;
 1002         bpage->datacount = size;
 1003         STAILQ_INSERT_TAIL(&(map->bpages), bpage, links);
 1004         return (bpage->busaddr);
 1005 }
 1006 
 1007 static void
 1008 free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage)
 1009 {
 1010         struct bus_dmamap *map;
 1011         struct bounce_zone *bz;
 1012 
 1013         bz = dmat->bounce_zone;
 1014         bpage->datavaddr = 0;
 1015         bpage->datacount = 0;
 1016         if (dmat->common.flags & BUS_DMA_KEEP_PG_OFFSET) {
 1017                 /*
 1018                  * Reset the bounce page to start at offset 0.  Other uses
 1019                  * of this bounce page may need to store a full page of
 1020                  * data and/or assume it starts on a page boundary.
 1021                  */
 1022                 bpage->vaddr &= ~PAGE_MASK;
 1023                 bpage->busaddr &= ~PAGE_MASK;
 1024         }
 1025 
 1026         mtx_lock(&bounce_lock);
 1027         STAILQ_INSERT_HEAD(&bz->bounce_page_list, bpage, links);
 1028         bz->free_bpages++;
 1029         bz->active_bpages--;
 1030         if ((map = STAILQ_FIRST(&bounce_map_waitinglist)) != NULL) {
 1031                 if (reserve_bounce_pages(map->dmat, map, 1) == 0) {
 1032                         STAILQ_REMOVE_HEAD(&bounce_map_waitinglist, links);
 1033                         STAILQ_INSERT_TAIL(&bounce_map_callbacklist,
 1034                             map, links);
 1035                         busdma_swi_pending = 1;
 1036                         bz->total_deferred++;
 1037                         swi_sched(vm_ih, 0);
 1038                 }
 1039         }
 1040         mtx_unlock(&bounce_lock);
 1041 }
 1042 
 1043 void
 1044 busdma_swi(void)
 1045 {
 1046         bus_dma_tag_t dmat;
 1047         struct bus_dmamap *map;
 1048 
 1049         mtx_lock(&bounce_lock);
 1050         while ((map = STAILQ_FIRST(&bounce_map_callbacklist)) != NULL) {
 1051                 STAILQ_REMOVE_HEAD(&bounce_map_callbacklist, links);
 1052                 mtx_unlock(&bounce_lock);
 1053                 dmat = map->dmat;
 1054                 (dmat->common.lockfunc)(dmat->common.lockfuncarg, BUS_DMA_LOCK);
 1055                 bus_dmamap_load_mem(map->dmat, map, &map->mem,
 1056                     map->callback, map->callback_arg, BUS_DMA_WAITOK);
 1057                 (dmat->common.lockfunc)(dmat->common.lockfuncarg,
 1058                     BUS_DMA_UNLOCK);
 1059                 mtx_lock(&bounce_lock);
 1060         }
 1061         mtx_unlock(&bounce_lock);
 1062 }
 1063 
 1064 struct bus_dma_impl bus_dma_bounce_impl = {
 1065         .tag_create = bounce_bus_dma_tag_create,
 1066         .tag_destroy = bounce_bus_dma_tag_destroy,
 1067         .map_create = bounce_bus_dmamap_create,
 1068         .map_destroy = bounce_bus_dmamap_destroy,
 1069         .mem_alloc = bounce_bus_dmamem_alloc,
 1070         .mem_free = bounce_bus_dmamem_free,
 1071         .load_phys = bounce_bus_dmamap_load_phys,
 1072         .load_buffer = bounce_bus_dmamap_load_buffer,
 1073         .load_ma = bus_dmamap_load_ma_triv,
 1074         .map_waitok = bounce_bus_dmamap_waitok,
 1075         .map_complete = bounce_bus_dmamap_complete,
 1076         .map_unload = bounce_bus_dmamap_unload,
 1077         .map_sync = bounce_bus_dmamap_sync
 1078 };

Cache object: 3055ec34ab65ef2c9cb172aa4edb327b


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.