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/arm64/arm64/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  * Copyright (c) 2015-2016 The FreeBSD Foundation
    4  * All rights reserved.
    5  *
    6  * Portions of this software were developed by Andrew Turner
    7  * under sponsorship of the FreeBSD Foundation.
    8  *
    9  * Portions of this software were developed by Semihalf
   10  * under sponsorship of the FreeBSD Foundation.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions, and the following disclaimer,
   17  *    without modification, immediately at the beginning of the file.
   18  * 2. The name of the author may not be used to endorse or promote products
   19  *    derived from this software without specific prior written permission.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   24  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
   25  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   31  * SUCH DAMAGE.
   32  */
   33 
   34 #include <sys/cdefs.h>
   35 __FBSDID("$FreeBSD: releng/11.2/sys/arm64/arm64/busdma_bounce.c 318976 2017-05-27 07:47:52Z hselasky $");
   36 
   37 #include <sys/param.h>
   38 #include <sys/systm.h>
   39 #include <sys/malloc.h>
   40 #include <sys/bus.h>
   41 #include <sys/interrupt.h>
   42 #include <sys/kernel.h>
   43 #include <sys/ktr.h>
   44 #include <sys/lock.h>
   45 #include <sys/proc.h>
   46 #include <sys/memdesc.h>
   47 #include <sys/mutex.h>
   48 #include <sys/sysctl.h>
   49 #include <sys/uio.h>
   50 
   51 #include <vm/vm.h>
   52 #include <vm/vm_extern.h>
   53 #include <vm/vm_kern.h>
   54 #include <vm/vm_page.h>
   55 #include <vm/vm_map.h>
   56 
   57 #include <machine/atomic.h>
   58 #include <machine/bus.h>
   59 #include <machine/md_var.h>
   60 #include <arm64/include/bus_dma_impl.h>
   61 
   62 #define MAX_BPAGES 4096
   63 
   64 enum {
   65         BF_COULD_BOUNCE         = 0x01,
   66         BF_MIN_ALLOC_COMP       = 0x02,
   67         BF_KMEM_ALLOC           = 0x04,
   68         BF_COHERENT             = 0x10,
   69 };
   70 
   71 struct bounce_zone;
   72 
   73 struct bus_dma_tag {
   74         struct bus_dma_tag_common common;
   75         int                     map_count;
   76         int                     bounce_flags;
   77         bus_dma_segment_t       *segments;
   78         struct bounce_zone      *bounce_zone;
   79 };
   80 
   81 struct bounce_page {
   82         vm_offset_t     vaddr;          /* kva of bounce buffer */
   83         bus_addr_t      busaddr;        /* Physical address */
   84         vm_offset_t     datavaddr;      /* kva of client data */
   85         vm_page_t       datapage;       /* physical page of client data */
   86         vm_offset_t     dataoffs;       /* page offset of client data */
   87         bus_size_t      datacount;      /* client data count */
   88         STAILQ_ENTRY(bounce_page) links;
   89 };
   90 
   91 int busdma_swi_pending;
   92 
   93 struct bounce_zone {
   94         STAILQ_ENTRY(bounce_zone) links;
   95         STAILQ_HEAD(bp_list, bounce_page) bounce_page_list;
   96         int             total_bpages;
   97         int             free_bpages;
   98         int             reserved_bpages;
   99         int             active_bpages;
  100         int             total_bounced;
  101         int             total_deferred;
  102         int             map_count;
  103         bus_size_t      alignment;
  104         bus_addr_t      lowaddr;
  105         char            zoneid[8];
  106         char            lowaddrid[20];
  107         struct sysctl_ctx_list sysctl_tree;
  108         struct sysctl_oid *sysctl_tree_top;
  109 };
  110 
  111 static struct mtx bounce_lock;
  112 static int total_bpages;
  113 static int busdma_zonecount;
  114 static STAILQ_HEAD(, bounce_zone) bounce_zone_list;
  115 
  116 static SYSCTL_NODE(_hw, OID_AUTO, busdma, CTLFLAG_RD, 0, "Busdma parameters");
  117 SYSCTL_INT(_hw_busdma, OID_AUTO, total_bpages, CTLFLAG_RD, &total_bpages, 0,
  118            "Total bounce pages");
  119 
  120 struct sync_list {
  121         vm_offset_t     vaddr;          /* kva of client data */
  122         bus_addr_t      paddr;          /* physical address */
  123         vm_page_t       pages;          /* starting page of client data */
  124         bus_size_t      datacount;      /* client data count */
  125 };
  126 
  127 struct bus_dmamap {
  128         struct bp_list         bpages;
  129         int                    pagesneeded;
  130         int                    pagesreserved;
  131         bus_dma_tag_t          dmat;
  132         struct memdesc         mem;
  133         bus_dmamap_callback_t *callback;
  134         void                  *callback_arg;
  135         STAILQ_ENTRY(bus_dmamap) links;
  136         u_int                   flags;
  137 #define DMAMAP_COULD_BOUNCE     (1 << 0)
  138 #define DMAMAP_FROM_DMAMEM      (1 << 1)
  139         int                     sync_count;
  140         struct sync_list        slist[];
  141 };
  142 
  143 static STAILQ_HEAD(, bus_dmamap) bounce_map_waitinglist;
  144 static STAILQ_HEAD(, bus_dmamap) bounce_map_callbacklist;
  145 
  146 static void init_bounce_pages(void *dummy);
  147 static int alloc_bounce_zone(bus_dma_tag_t dmat);
  148 static int alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages);
  149 static int reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
  150     int commit);
  151 static bus_addr_t add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map,
  152     vm_offset_t vaddr, bus_addr_t addr, bus_size_t size);
  153 static void free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage);
  154 int run_filter(bus_dma_tag_t dmat, bus_addr_t paddr);
  155 static void _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
  156     pmap_t pmap, void *buf, bus_size_t buflen, int flags);
  157 static void _bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map,
  158     vm_paddr_t buf, bus_size_t buflen, int flags);
  159 static int _bus_dmamap_reserve_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
  160     int flags);
  161 
  162 /*
  163  * Allocate a device specific dma_tag.
  164  */
  165 static int
  166 bounce_bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
  167     bus_addr_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr,
  168     bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize,
  169     int nsegments, bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
  170     void *lockfuncarg, bus_dma_tag_t *dmat)
  171 {
  172         bus_dma_tag_t newtag;
  173         int error;
  174 
  175         *dmat = NULL;
  176         error = common_bus_dma_tag_create(parent != NULL ? &parent->common :
  177             NULL, alignment, boundary, lowaddr, highaddr, filter, filterarg,
  178             maxsize, nsegments, maxsegsz, flags, lockfunc, lockfuncarg,
  179             sizeof (struct bus_dma_tag), (void **)&newtag);
  180         if (error != 0)
  181                 return (error);
  182 
  183         newtag->common.impl = &bus_dma_bounce_impl;
  184         newtag->map_count = 0;
  185         newtag->segments = NULL;
  186 
  187         if ((flags & BUS_DMA_COHERENT) != 0)
  188                 newtag->bounce_flags |= BF_COHERENT;
  189 
  190         if (parent != NULL) {
  191                 if ((newtag->common.filter != NULL ||
  192                     (parent->bounce_flags & BF_COULD_BOUNCE) != 0))
  193                         newtag->bounce_flags |= BF_COULD_BOUNCE;
  194 
  195                 /* Copy some flags from the parent */
  196                 newtag->bounce_flags |= parent->bounce_flags & BF_COHERENT;
  197         }
  198 
  199         if (newtag->common.lowaddr < ptoa((vm_paddr_t)Maxmem) ||
  200             newtag->common.alignment > 1)
  201                 newtag->bounce_flags |= BF_COULD_BOUNCE;
  202 
  203         if (((newtag->bounce_flags & BF_COULD_BOUNCE) != 0) &&
  204             (flags & BUS_DMA_ALLOCNOW) != 0) {
  205                 struct bounce_zone *bz;
  206 
  207                 /* Must bounce */
  208                 if ((error = alloc_bounce_zone(newtag)) != 0) {
  209                         free(newtag, M_DEVBUF);
  210                         return (error);
  211                 }
  212                 bz = newtag->bounce_zone;
  213 
  214                 if (ptoa(bz->total_bpages) < maxsize) {
  215                         int pages;
  216 
  217                         pages = atop(maxsize) - bz->total_bpages;
  218 
  219                         /* Add pages to our bounce pool */
  220                         if (alloc_bounce_pages(newtag, pages) < pages)
  221                                 error = ENOMEM;
  222                 }
  223                 /* Performed initial allocation */
  224                 newtag->bounce_flags |= BF_MIN_ALLOC_COMP;
  225         } else
  226                 error = 0;
  227 
  228         if (error != 0)
  229                 free(newtag, M_DEVBUF);
  230         else
  231                 *dmat = newtag;
  232         CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d",
  233             __func__, newtag, (newtag != NULL ? newtag->common.flags : 0),
  234             error);
  235         return (error);
  236 }
  237 
  238 static int
  239 bounce_bus_dma_tag_destroy(bus_dma_tag_t dmat)
  240 {
  241         bus_dma_tag_t dmat_copy, parent;
  242         int error;
  243 
  244         error = 0;
  245         dmat_copy = dmat;
  246 
  247         if (dmat != NULL) {
  248                 if (dmat->map_count != 0) {
  249                         error = EBUSY;
  250                         goto out;
  251                 }
  252                 while (dmat != NULL) {
  253                         parent = (bus_dma_tag_t)dmat->common.parent;
  254                         atomic_subtract_int(&dmat->common.ref_count, 1);
  255                         if (dmat->common.ref_count == 0) {
  256                                 if (dmat->segments != NULL)
  257                                         free(dmat->segments, M_DEVBUF);
  258                                 free(dmat, M_DEVBUF);
  259                                 /*
  260                                  * Last reference count, so
  261                                  * release our reference
  262                                  * count on our parent.
  263                                  */
  264                                 dmat = parent;
  265                         } else
  266                                 dmat = NULL;
  267                 }
  268         }
  269 out:
  270         CTR3(KTR_BUSDMA, "%s tag %p error %d", __func__, dmat_copy, error);
  271         return (error);
  272 }
  273 
  274 static bus_dmamap_t
  275 alloc_dmamap(bus_dma_tag_t dmat, int flags)
  276 {
  277         u_long mapsize;
  278         bus_dmamap_t map;
  279 
  280         mapsize = sizeof(*map);
  281         mapsize += sizeof(struct sync_list) * dmat->common.nsegments;
  282         map = malloc(mapsize, M_DEVBUF, flags | M_ZERO);
  283         if (map == NULL)
  284                 return (NULL);
  285 
  286         /* Initialize the new map */
  287         STAILQ_INIT(&map->bpages);
  288 
  289         return (map);
  290 }
  291 
  292 /*
  293  * Allocate a handle for mapping from kva/uva/physical
  294  * address space into bus device space.
  295  */
  296 static int
  297 bounce_bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)
  298 {
  299         struct bounce_zone *bz;
  300         int error, maxpages, pages;
  301 
  302         error = 0;
  303 
  304         if (dmat->segments == NULL) {
  305                 dmat->segments = (bus_dma_segment_t *)malloc(
  306                     sizeof(bus_dma_segment_t) * dmat->common.nsegments,
  307                     M_DEVBUF, M_NOWAIT);
  308                 if (dmat->segments == NULL) {
  309                         CTR3(KTR_BUSDMA, "%s: tag %p error %d",
  310                             __func__, dmat, ENOMEM);
  311                         return (ENOMEM);
  312                 }
  313         }
  314 
  315         *mapp = alloc_dmamap(dmat, M_NOWAIT);
  316         if (*mapp == NULL) {
  317                 CTR3(KTR_BUSDMA, "%s: tag %p error %d",
  318                     __func__, dmat, ENOMEM);
  319                 return (ENOMEM);
  320         }
  321 
  322         /*
  323          * Bouncing might be required if the driver asks for an active
  324          * exclusion region, a data alignment that is stricter than 1, and/or
  325          * an active address boundary.
  326          */
  327         if (dmat->bounce_flags & BF_COULD_BOUNCE) {
  328                 /* Must bounce */
  329                 if (dmat->bounce_zone == NULL) {
  330                         if ((error = alloc_bounce_zone(dmat)) != 0) {
  331                                 free(*mapp, M_DEVBUF);
  332                                 return (error);
  333                         }
  334                 }
  335                 bz = dmat->bounce_zone;
  336 
  337                 (*mapp)->flags = DMAMAP_COULD_BOUNCE;
  338 
  339                 /*
  340                  * Attempt to add pages to our pool on a per-instance
  341                  * basis up to a sane limit.
  342                  */
  343                 if (dmat->common.alignment > 1)
  344                         maxpages = MAX_BPAGES;
  345                 else
  346                         maxpages = MIN(MAX_BPAGES, Maxmem -
  347                             atop(dmat->common.lowaddr));
  348                 if ((dmat->bounce_flags & BF_MIN_ALLOC_COMP) == 0 ||
  349                     (bz->map_count > 0 && bz->total_bpages < maxpages)) {
  350                         pages = MAX(atop(dmat->common.maxsize), 1);
  351                         pages = MIN(maxpages - bz->total_bpages, pages);
  352                         pages = MAX(pages, 1);
  353                         if (alloc_bounce_pages(dmat, pages) < pages)
  354                                 error = ENOMEM;
  355                         if ((dmat->bounce_flags & BF_MIN_ALLOC_COMP)
  356                             == 0) {
  357                                 if (error == 0) {
  358                                         dmat->bounce_flags |=
  359                                             BF_MIN_ALLOC_COMP;
  360                                 }
  361                         } else
  362                                 error = 0;
  363                 }
  364                 bz->map_count++;
  365         }
  366         if (error == 0)
  367                 dmat->map_count++;
  368         else
  369                 free(*mapp, M_DEVBUF);
  370         CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  371             __func__, dmat, dmat->common.flags, error);
  372         return (error);
  373 }
  374 
  375 /*
  376  * Destroy a handle for mapping from kva/uva/physical
  377  * address space into bus device space.
  378  */
  379 static int
  380 bounce_bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map)
  381 {
  382 
  383         /* Check we are destroying the correct map type */
  384         if ((map->flags & DMAMAP_FROM_DMAMEM) != 0)
  385                 panic("bounce_bus_dmamap_destroy: Invalid map freed\n");
  386 
  387         if (STAILQ_FIRST(&map->bpages) != NULL || map->sync_count != 0) {
  388                 CTR3(KTR_BUSDMA, "%s: tag %p error %d", __func__, dmat, EBUSY);
  389                 return (EBUSY);
  390         }
  391         if (dmat->bounce_zone) {
  392                 KASSERT((map->flags & DMAMAP_COULD_BOUNCE) != 0,
  393                     ("%s: Bounce zone when cannot bounce", __func__));
  394                 dmat->bounce_zone->map_count--;
  395         }
  396         free(map, M_DEVBUF);
  397         dmat->map_count--;
  398         CTR2(KTR_BUSDMA, "%s: tag %p error 0", __func__, dmat);
  399         return (0);
  400 }
  401 
  402 
  403 /*
  404  * Allocate a piece of memory that can be efficiently mapped into
  405  * bus device space based on the constraints lited in the dma tag.
  406  * A dmamap to for use with dmamap_load is also allocated.
  407  */
  408 static int
  409 bounce_bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
  410     bus_dmamap_t *mapp)
  411 {
  412         /*
  413          * XXX ARM64TODO:
  414          * This bus_dma implementation requires IO-Coherent architecutre.
  415          * If IO-Coherency is not guaranteed, the BUS_DMA_COHERENT flag has
  416          * to be implented using non-cacheable memory.
  417          */
  418 
  419         vm_memattr_t attr;
  420         int mflags;
  421 
  422         if (flags & BUS_DMA_NOWAIT)
  423                 mflags = M_NOWAIT;
  424         else
  425                 mflags = M_WAITOK;
  426 
  427         if (dmat->segments == NULL) {
  428                 dmat->segments = (bus_dma_segment_t *)malloc(
  429                     sizeof(bus_dma_segment_t) * dmat->common.nsegments,
  430                     M_DEVBUF, mflags);
  431                 if (dmat->segments == NULL) {
  432                         CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  433                             __func__, dmat, dmat->common.flags, ENOMEM);
  434                         return (ENOMEM);
  435                 }
  436         }
  437         if (flags & BUS_DMA_ZERO)
  438                 mflags |= M_ZERO;
  439         if (flags & BUS_DMA_NOCACHE)
  440                 attr = VM_MEMATTR_UNCACHEABLE;
  441         else if ((flags & BUS_DMA_COHERENT) != 0 &&
  442             (dmat->bounce_flags & BF_COHERENT) == 0)
  443                 /*
  444                  * If we have a non-coherent tag, and are trying to allocate
  445                  * a coherent block of memory it needs to be uncached.
  446                  */
  447                 attr = VM_MEMATTR_UNCACHEABLE;
  448         else
  449                 attr = VM_MEMATTR_DEFAULT;
  450 
  451         /*
  452          * Create the map, but don't set the could bounce flag as
  453          * this allocation should never bounce;
  454          */
  455         *mapp = alloc_dmamap(dmat, mflags);
  456         if (*mapp == NULL) {
  457                 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  458                     __func__, dmat, dmat->common.flags, ENOMEM);
  459                 return (ENOMEM);
  460         }
  461         (*mapp)->flags = DMAMAP_FROM_DMAMEM;
  462 
  463         /*
  464          * Allocate the buffer from the malloc(9) allocator if...
  465          *  - It's small enough to fit into a single power of two sized bucket.
  466          *  - The alignment is less than or equal to the maximum size
  467          *  - The low address requirement is fulfilled.
  468          * else allocate non-contiguous pages if...
  469          *  - The page count that could get allocated doesn't exceed
  470          *    nsegments also when the maximum segment size is less
  471          *    than PAGE_SIZE.
  472          *  - The alignment constraint isn't larger than a page boundary.
  473          *  - There are no boundary-crossing constraints.
  474          * else allocate a block of contiguous pages because one or more of the
  475          * constraints is something that only the contig allocator can fulfill.
  476          *
  477          * NOTE: The (dmat->common.alignment <= dmat->maxsize) check
  478          * below is just a quick hack. The exact alignment guarantees
  479          * of malloc(9) need to be nailed down, and the code below
  480          * should be rewritten to take that into account.
  481          *
  482          * In the meantime warn the user if malloc gets it wrong.
  483          */
  484         if ((dmat->common.maxsize <= PAGE_SIZE) &&
  485            (dmat->common.alignment <= dmat->common.maxsize) &&
  486             dmat->common.lowaddr >= ptoa((vm_paddr_t)Maxmem) &&
  487             attr == VM_MEMATTR_DEFAULT) {
  488                 *vaddr = malloc(dmat->common.maxsize, M_DEVBUF, mflags);
  489         } else if (dmat->common.nsegments >=
  490             howmany(dmat->common.maxsize, MIN(dmat->common.maxsegsz, PAGE_SIZE)) &&
  491             dmat->common.alignment <= PAGE_SIZE &&
  492             (dmat->common.boundary % PAGE_SIZE) == 0) {
  493                 /* Page-based multi-segment allocations allowed */
  494                 *vaddr = (void *)kmem_alloc_attr(kernel_arena,
  495                     dmat->common.maxsize, mflags, 0ul, dmat->common.lowaddr,
  496                     attr);
  497                 dmat->bounce_flags |= BF_KMEM_ALLOC;
  498         } else {
  499                 *vaddr = (void *)kmem_alloc_contig(kernel_arena,
  500                     dmat->common.maxsize, mflags, 0ul, dmat->common.lowaddr,
  501                     dmat->common.alignment != 0 ? dmat->common.alignment : 1ul,
  502                     dmat->common.boundary, attr);
  503                 dmat->bounce_flags |= BF_KMEM_ALLOC;
  504         }
  505         if (*vaddr == NULL) {
  506                 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  507                     __func__, dmat, dmat->common.flags, ENOMEM);
  508                 free(*mapp, M_DEVBUF);
  509                 return (ENOMEM);
  510         } else if (vtophys(*vaddr) & (dmat->common.alignment - 1)) {
  511                 printf("bus_dmamem_alloc failed to align memory properly.\n");
  512         }
  513         dmat->map_count++;
  514         CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  515             __func__, dmat, dmat->common.flags, 0);
  516         return (0);
  517 }
  518 
  519 /*
  520  * Free a piece of memory and it's allociated dmamap, that was allocated
  521  * via bus_dmamem_alloc.  Make the same choice for free/contigfree.
  522  */
  523 static void
  524 bounce_bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)
  525 {
  526 
  527         /*
  528          * Check the map came from bounce_bus_dmamem_alloc, so the map
  529          * should be NULL and the BF_KMEM_ALLOC flag cleared if malloc()
  530          * was used and set if kmem_alloc_contig() was used.
  531          */
  532         if ((map->flags & DMAMAP_FROM_DMAMEM) == 0)
  533                 panic("bus_dmamem_free: Invalid map freed\n");
  534         if ((dmat->bounce_flags & BF_KMEM_ALLOC) == 0)
  535                 free(vaddr, M_DEVBUF);
  536         else
  537                 kmem_free(kernel_arena, (vm_offset_t)vaddr,
  538                     dmat->common.maxsize);
  539         free(map, M_DEVBUF);
  540         dmat->map_count--;
  541         CTR3(KTR_BUSDMA, "%s: tag %p flags 0x%x", __func__, dmat,
  542             dmat->bounce_flags);
  543 }
  544 
  545 static void
  546 _bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map, vm_paddr_t buf,
  547     bus_size_t buflen, int flags)
  548 {
  549         bus_addr_t curaddr;
  550         bus_size_t sgsize;
  551 
  552         if ((map->flags & DMAMAP_COULD_BOUNCE) != 0 && map->pagesneeded == 0) {
  553                 /*
  554                  * Count the number of bounce pages
  555                  * needed in order to complete this transfer
  556                  */
  557                 curaddr = buf;
  558                 while (buflen != 0) {
  559                         sgsize = MIN(buflen, dmat->common.maxsegsz);
  560                         if (bus_dma_run_filter(&dmat->common, curaddr)) {
  561                                 sgsize = MIN(sgsize,
  562                                     PAGE_SIZE - (curaddr & PAGE_MASK));
  563                                 map->pagesneeded++;
  564                         }
  565                         curaddr += sgsize;
  566                         buflen -= sgsize;
  567                 }
  568                 CTR1(KTR_BUSDMA, "pagesneeded= %d\n", map->pagesneeded);
  569         }
  570 }
  571 
  572 static void
  573 _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map, pmap_t pmap,
  574     void *buf, bus_size_t buflen, int flags)
  575 {
  576         vm_offset_t vaddr;
  577         vm_offset_t vendaddr;
  578         bus_addr_t paddr;
  579         bus_size_t sg_len;
  580 
  581         if ((map->flags & DMAMAP_COULD_BOUNCE) != 0 && map->pagesneeded == 0) {
  582                 CTR4(KTR_BUSDMA, "lowaddr= %d Maxmem= %d, boundary= %d, "
  583                     "alignment= %d", dmat->common.lowaddr,
  584                     ptoa((vm_paddr_t)Maxmem),
  585                     dmat->common.boundary, dmat->common.alignment);
  586                 CTR2(KTR_BUSDMA, "map= %p, pagesneeded= %d", map,
  587                     map->pagesneeded);
  588                 /*
  589                  * Count the number of bounce pages
  590                  * needed in order to complete this transfer
  591                  */
  592                 vaddr = (vm_offset_t)buf;
  593                 vendaddr = (vm_offset_t)buf + buflen;
  594 
  595                 while (vaddr < vendaddr) {
  596                         sg_len = PAGE_SIZE - ((vm_offset_t)vaddr & PAGE_MASK);
  597                         if (pmap == kernel_pmap)
  598                                 paddr = pmap_kextract(vaddr);
  599                         else
  600                                 paddr = pmap_extract(pmap, vaddr);
  601                         if (bus_dma_run_filter(&dmat->common, paddr) != 0) {
  602                                 sg_len = roundup2(sg_len,
  603                                     dmat->common.alignment);
  604                                 map->pagesneeded++;
  605                         }
  606                         vaddr += sg_len;
  607                 }
  608                 CTR1(KTR_BUSDMA, "pagesneeded= %d\n", map->pagesneeded);
  609         }
  610 }
  611 
  612 static int
  613 _bus_dmamap_reserve_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int flags)
  614 {
  615 
  616         /* Reserve Necessary Bounce Pages */
  617         mtx_lock(&bounce_lock);
  618         if (flags & BUS_DMA_NOWAIT) {
  619                 if (reserve_bounce_pages(dmat, map, 0) != 0) {
  620                         mtx_unlock(&bounce_lock);
  621                         return (ENOMEM);
  622                 }
  623         } else {
  624                 if (reserve_bounce_pages(dmat, map, 1) != 0) {
  625                         /* Queue us for resources */
  626                         STAILQ_INSERT_TAIL(&bounce_map_waitinglist, map, links);
  627                         mtx_unlock(&bounce_lock);
  628                         return (EINPROGRESS);
  629                 }
  630         }
  631         mtx_unlock(&bounce_lock);
  632 
  633         return (0);
  634 }
  635 
  636 /*
  637  * Add a single contiguous physical range to the segment list.
  638  */
  639 static int
  640 _bus_dmamap_addseg(bus_dma_tag_t dmat, bus_dmamap_t map, bus_addr_t curaddr,
  641     bus_size_t sgsize, bus_dma_segment_t *segs, int *segp)
  642 {
  643         bus_addr_t baddr, bmask;
  644         int seg;
  645 
  646         /*
  647          * Make sure we don't cross any boundaries.
  648          */
  649         bmask = ~(dmat->common.boundary - 1);
  650         if (dmat->common.boundary > 0) {
  651                 baddr = (curaddr + dmat->common.boundary) & bmask;
  652                 if (sgsize > (baddr - curaddr))
  653                         sgsize = (baddr - curaddr);
  654         }
  655 
  656         /*
  657          * Insert chunk into a segment, coalescing with
  658          * previous segment if possible.
  659          */
  660         seg = *segp;
  661         if (seg == -1) {
  662                 seg = 0;
  663                 segs[seg].ds_addr = curaddr;
  664                 segs[seg].ds_len = sgsize;
  665         } else {
  666                 if (curaddr == segs[seg].ds_addr + segs[seg].ds_len &&
  667                     (segs[seg].ds_len + sgsize) <= dmat->common.maxsegsz &&
  668                     (dmat->common.boundary == 0 ||
  669                      (segs[seg].ds_addr & bmask) == (curaddr & bmask)))
  670                         segs[seg].ds_len += sgsize;
  671                 else {
  672                         if (++seg >= dmat->common.nsegments)
  673                                 return (0);
  674                         segs[seg].ds_addr = curaddr;
  675                         segs[seg].ds_len = sgsize;
  676                 }
  677         }
  678         *segp = seg;
  679         return (sgsize);
  680 }
  681 
  682 /*
  683  * Utility function to load a physical buffer.  segp contains
  684  * the starting segment on entrace, and the ending segment on exit.
  685  */
  686 static int
  687 bounce_bus_dmamap_load_phys(bus_dma_tag_t dmat, bus_dmamap_t map,
  688     vm_paddr_t buf, bus_size_t buflen, int flags, bus_dma_segment_t *segs,
  689     int *segp)
  690 {
  691         struct sync_list *sl;
  692         bus_size_t sgsize;
  693         bus_addr_t curaddr, sl_end;
  694         int error;
  695 
  696         if (segs == NULL)
  697                 segs = dmat->segments;
  698 
  699         if ((dmat->bounce_flags & BF_COULD_BOUNCE) != 0) {
  700                 _bus_dmamap_count_phys(dmat, map, buf, buflen, flags);
  701                 if (map->pagesneeded != 0) {
  702                         error = _bus_dmamap_reserve_pages(dmat, map, flags);
  703                         if (error)
  704                                 return (error);
  705                 }
  706         }
  707 
  708         sl = map->slist + map->sync_count - 1;
  709         sl_end = 0;
  710 
  711         while (buflen > 0) {
  712                 curaddr = buf;
  713                 sgsize = MIN(buflen, dmat->common.maxsegsz);
  714                 if (((dmat->bounce_flags & BF_COULD_BOUNCE) != 0) &&
  715                     map->pagesneeded != 0 &&
  716                     bus_dma_run_filter(&dmat->common, curaddr)) {
  717                         sgsize = MIN(sgsize, PAGE_SIZE - (curaddr & PAGE_MASK));
  718                         curaddr = add_bounce_page(dmat, map, 0, curaddr,
  719                             sgsize);
  720                 } else if ((dmat->bounce_flags & BF_COHERENT) == 0) {
  721                         if (map->sync_count > 0)
  722                                 sl_end = sl->paddr + sl->datacount;
  723 
  724                         if (map->sync_count == 0 || curaddr != sl_end) {
  725                                 if (++map->sync_count > dmat->common.nsegments)
  726                                         break;
  727                                 sl++;
  728                                 sl->vaddr = 0;
  729                                 sl->paddr = curaddr;
  730                                 sl->datacount = sgsize;
  731                                 sl->pages = PHYS_TO_VM_PAGE(curaddr);
  732                                 KASSERT(sl->pages != NULL,
  733                                     ("%s: page at PA:0x%08lx is not in "
  734                                     "vm_page_array", __func__, curaddr));
  735                         } else
  736                                 sl->datacount += sgsize;
  737                 }
  738                 sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs,
  739                     segp);
  740                 if (sgsize == 0)
  741                         break;
  742                 buf += sgsize;
  743                 buflen -= sgsize;
  744         }
  745 
  746         /*
  747          * Did we fit?
  748          */
  749         return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */
  750 }
  751 
  752 /*
  753  * Utility function to load a linear buffer.  segp contains
  754  * the starting segment on entrace, and the ending segment on exit.
  755  */
  756 static int
  757 bounce_bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf,
  758     bus_size_t buflen, pmap_t pmap, int flags, bus_dma_segment_t *segs,
  759     int *segp)
  760 {
  761         struct sync_list *sl;
  762         bus_size_t sgsize, max_sgsize;
  763         bus_addr_t curaddr, sl_pend;
  764         vm_offset_t kvaddr, vaddr, sl_vend;
  765         int error;
  766 
  767         if (segs == NULL)
  768                 segs = dmat->segments;
  769 
  770         if ((dmat->bounce_flags & BF_COULD_BOUNCE) != 0) {
  771                 _bus_dmamap_count_pages(dmat, map, pmap, buf, buflen, flags);
  772                 if (map->pagesneeded != 0) {
  773                         error = _bus_dmamap_reserve_pages(dmat, map, flags);
  774                         if (error)
  775                                 return (error);
  776                 }
  777         }
  778 
  779         sl = map->slist + map->sync_count - 1;
  780         vaddr = (vm_offset_t)buf;
  781         sl_pend = 0;
  782         sl_vend = 0;
  783 
  784         while (buflen > 0) {
  785                 /*
  786                  * Get the physical address for this segment.
  787                  */
  788                 if (pmap == kernel_pmap) {
  789                         curaddr = pmap_kextract(vaddr);
  790                         kvaddr = vaddr;
  791                 } else {
  792                         curaddr = pmap_extract(pmap, vaddr);
  793                         kvaddr = 0;
  794                 }
  795 
  796                 /*
  797                  * Compute the segment size, and adjust counts.
  798                  */
  799                 max_sgsize = MIN(buflen, dmat->common.maxsegsz);
  800                 sgsize = PAGE_SIZE - (curaddr & PAGE_MASK);
  801                 if (((dmat->bounce_flags & BF_COULD_BOUNCE) != 0) &&
  802                     map->pagesneeded != 0 &&
  803                     bus_dma_run_filter(&dmat->common, curaddr)) {
  804                         sgsize = roundup2(sgsize, dmat->common.alignment);
  805                         sgsize = MIN(sgsize, max_sgsize);
  806                         curaddr = add_bounce_page(dmat, map, kvaddr, curaddr,
  807                             sgsize);
  808                 } else if ((dmat->bounce_flags & BF_COHERENT) == 0) {
  809                         sgsize = MIN(sgsize, max_sgsize);
  810                         if (map->sync_count > 0) {
  811                                 sl_pend = sl->paddr + sl->datacount;
  812                                 sl_vend = sl->vaddr + sl->datacount;
  813                         }
  814 
  815                         if (map->sync_count == 0 ||
  816                             (kvaddr != 0 && kvaddr != sl_vend) ||
  817                             (curaddr != sl_pend)) {
  818 
  819                                 if (++map->sync_count > dmat->common.nsegments)
  820                                         goto cleanup;
  821                                 sl++;
  822                                 sl->vaddr = kvaddr;
  823                                 sl->paddr = curaddr;
  824                                 if (kvaddr != 0) {
  825                                         sl->pages = NULL;
  826                                 } else {
  827                                         sl->pages = PHYS_TO_VM_PAGE(curaddr);
  828                                         KASSERT(sl->pages != NULL,
  829                                             ("%s: page at PA:0x%08lx is not "
  830                                             "in vm_page_array", __func__,
  831                                             curaddr));
  832                                 }
  833                                 sl->datacount = sgsize;
  834                         } else
  835                                 sl->datacount += sgsize;
  836                 } else {
  837                         sgsize = MIN(sgsize, max_sgsize);
  838                 }
  839                 sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs,
  840                     segp);
  841                 if (sgsize == 0)
  842                         break;
  843                 vaddr += sgsize;
  844                 buflen -= sgsize;
  845         }
  846 
  847 cleanup:
  848         /*
  849          * Did we fit?
  850          */
  851         return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */
  852 }
  853 
  854 static void
  855 bounce_bus_dmamap_waitok(bus_dma_tag_t dmat, bus_dmamap_t map,
  856     struct memdesc *mem, bus_dmamap_callback_t *callback, void *callback_arg)
  857 {
  858 
  859         if ((map->flags & DMAMAP_COULD_BOUNCE) == 0)
  860                 return;
  861         map->mem = *mem;
  862         map->dmat = dmat;
  863         map->callback = callback;
  864         map->callback_arg = callback_arg;
  865 }
  866 
  867 static bus_dma_segment_t *
  868 bounce_bus_dmamap_complete(bus_dma_tag_t dmat, bus_dmamap_t map,
  869     bus_dma_segment_t *segs, int nsegs, int error)
  870 {
  871 
  872         if (segs == NULL)
  873                 segs = dmat->segments;
  874         return (segs);
  875 }
  876 
  877 /*
  878  * Release the mapping held by map.
  879  */
  880 static void
  881 bounce_bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
  882 {
  883         struct bounce_page *bpage;
  884 
  885         while ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
  886                 STAILQ_REMOVE_HEAD(&map->bpages, links);
  887                 free_bounce_page(dmat, bpage);
  888         }
  889 
  890         map->sync_count = 0;
  891 }
  892 
  893 static void
  894 dma_preread_safe(vm_offset_t va, vm_size_t size)
  895 {
  896         /*
  897          * Write back any partial cachelines immediately before and
  898          * after the DMA region.
  899          */
  900         if (va & (dcache_line_size - 1))
  901                 cpu_dcache_wb_range(va, 1);
  902         if ((va + size) & (dcache_line_size - 1))
  903                 cpu_dcache_wb_range(va + size, 1);
  904 
  905         cpu_dcache_inv_range(va, size);
  906 }
  907 
  908 static void
  909 dma_dcache_sync(struct sync_list *sl, bus_dmasync_op_t op)
  910 {
  911         uint32_t len, offset;
  912         vm_page_t m;
  913         vm_paddr_t pa;
  914         vm_offset_t va, tempva;
  915         bus_size_t size;
  916 
  917         offset = sl->paddr & PAGE_MASK;
  918         m = sl->pages;
  919         size = sl->datacount;
  920         pa = sl->paddr;
  921 
  922         for ( ; size != 0; size -= len, pa += len, offset = 0, ++m) {
  923                 tempva = 0;
  924                 if (sl->vaddr == 0) {
  925                         len = min(PAGE_SIZE - offset, size);
  926                         tempva = pmap_quick_enter_page(m);
  927                         va = tempva | offset;
  928                         KASSERT(pa == (VM_PAGE_TO_PHYS(m) | offset),
  929                             ("unexpected vm_page_t phys: 0x%16lx != 0x%16lx",
  930                             VM_PAGE_TO_PHYS(m) | offset, pa));
  931                 } else {
  932                         len = sl->datacount;
  933                         va = sl->vaddr;
  934                 }
  935 
  936                 switch (op) {
  937                 case BUS_DMASYNC_PREWRITE:
  938                 case BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD:
  939                         cpu_dcache_wb_range(va, len);
  940                         break;
  941                 case BUS_DMASYNC_PREREAD:
  942                         /*
  943                          * An mbuf may start in the middle of a cacheline. There
  944                          * will be no cpu writes to the beginning of that line
  945                          * (which contains the mbuf header) while dma is in
  946                          * progress.  Handle that case by doing a writeback of
  947                          * just the first cacheline before invalidating the
  948                          * overall buffer.  Any mbuf in a chain may have this
  949                          * misalignment.  Buffers which are not mbufs bounce if
  950                          * they are not aligned to a cacheline.
  951                          */
  952                         dma_preread_safe(va, len);
  953                         break;
  954                 case BUS_DMASYNC_POSTREAD:
  955                 case BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE:
  956                         cpu_dcache_inv_range(va, len);
  957                         break;
  958                 default:
  959                         panic("unsupported combination of sync operations: "
  960                               "0x%08x\n", op);
  961                 }
  962 
  963                 if (tempva != 0)
  964                         pmap_quick_remove_page(tempva);
  965         }
  966 }
  967 
  968 static void
  969 bounce_bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map,
  970     bus_dmasync_op_t op)
  971 {
  972         struct bounce_page *bpage;
  973         struct sync_list *sl, *end;
  974         vm_offset_t datavaddr, tempvaddr;
  975 
  976         if (op == BUS_DMASYNC_POSTWRITE)
  977                 return;
  978 
  979         if ((op & BUS_DMASYNC_POSTREAD) != 0) {
  980                 /*
  981                  * Wait for any DMA operations to complete before the bcopy.
  982                  */
  983                 dsb(sy);
  984         }
  985 
  986         if ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
  987                 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x op 0x%x "
  988                     "performing bounce", __func__, dmat, dmat->common.flags,
  989                     op);
  990 
  991                 if ((op & BUS_DMASYNC_PREWRITE) != 0) {
  992                         while (bpage != NULL) {
  993                                 tempvaddr = 0;
  994                                 datavaddr = bpage->datavaddr;
  995                                 if (datavaddr == 0) {
  996                                         tempvaddr = pmap_quick_enter_page(
  997                                             bpage->datapage);
  998                                         datavaddr = tempvaddr | bpage->dataoffs;
  999                                 }
 1000 
 1001                                 bcopy((void *)datavaddr,
 1002                                     (void *)bpage->vaddr, bpage->datacount);
 1003                                 if (tempvaddr != 0)
 1004                                         pmap_quick_remove_page(tempvaddr);
 1005                                 if ((dmat->bounce_flags & BF_COHERENT) == 0)
 1006                                         cpu_dcache_wb_range(bpage->vaddr,
 1007                                             bpage->datacount);
 1008                                 bpage = STAILQ_NEXT(bpage, links);
 1009                         }
 1010                         dmat->bounce_zone->total_bounced++;
 1011                 } else if ((op & BUS_DMASYNC_PREREAD) != 0) {
 1012                         while (bpage != NULL) {
 1013                                 if ((dmat->bounce_flags & BF_COHERENT) == 0)
 1014                                         cpu_dcache_wbinv_range(bpage->vaddr,
 1015                                             bpage->datacount);
 1016                                 bpage = STAILQ_NEXT(bpage, links);
 1017                         }
 1018                 }
 1019 
 1020                 if ((op & BUS_DMASYNC_POSTREAD) != 0) {
 1021                         while (bpage != NULL) {
 1022                                 if ((dmat->bounce_flags & BF_COHERENT) == 0)
 1023                                         cpu_dcache_inv_range(bpage->vaddr,
 1024                                             bpage->datacount);
 1025                                 tempvaddr = 0;
 1026                                 datavaddr = bpage->datavaddr;
 1027                                 if (datavaddr == 0) {
 1028                                         tempvaddr = pmap_quick_enter_page(
 1029                                             bpage->datapage);
 1030                                         datavaddr = tempvaddr | bpage->dataoffs;
 1031                                 }
 1032 
 1033                                 bcopy((void *)bpage->vaddr,
 1034                                     (void *)datavaddr, bpage->datacount);
 1035 
 1036                                 if (tempvaddr != 0)
 1037                                         pmap_quick_remove_page(tempvaddr);
 1038                                 bpage = STAILQ_NEXT(bpage, links);
 1039                         }
 1040                         dmat->bounce_zone->total_bounced++;
 1041                 }
 1042         }
 1043 
 1044         /*
 1045          * Cache maintenance for normal (non-COHERENT non-bounce) buffers.
 1046          */
 1047         if (map->sync_count != 0) {
 1048                 sl = &map->slist[0];
 1049                 end = &map->slist[map->sync_count];
 1050                 CTR3(KTR_BUSDMA, "%s: tag %p op 0x%x "
 1051                     "performing sync", __func__, dmat, op);
 1052 
 1053                 for ( ; sl != end; ++sl)
 1054                         dma_dcache_sync(sl, op);
 1055         }
 1056 
 1057         if ((op & (BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE)) != 0) {
 1058                 /*
 1059                  * Wait for the bcopy to complete before any DMA operations.
 1060                  */
 1061                 dsb(sy);
 1062         }
 1063 }
 1064 
 1065 static void
 1066 init_bounce_pages(void *dummy __unused)
 1067 {
 1068 
 1069         total_bpages = 0;
 1070         STAILQ_INIT(&bounce_zone_list);
 1071         STAILQ_INIT(&bounce_map_waitinglist);
 1072         STAILQ_INIT(&bounce_map_callbacklist);
 1073         mtx_init(&bounce_lock, "bounce pages lock", NULL, MTX_DEF);
 1074 }
 1075 SYSINIT(bpages, SI_SUB_LOCK, SI_ORDER_ANY, init_bounce_pages, NULL);
 1076 
 1077 static struct sysctl_ctx_list *
 1078 busdma_sysctl_tree(struct bounce_zone *bz)
 1079 {
 1080 
 1081         return (&bz->sysctl_tree);
 1082 }
 1083 
 1084 static struct sysctl_oid *
 1085 busdma_sysctl_tree_top(struct bounce_zone *bz)
 1086 {
 1087 
 1088         return (bz->sysctl_tree_top);
 1089 }
 1090 
 1091 static int
 1092 alloc_bounce_zone(bus_dma_tag_t dmat)
 1093 {
 1094         struct bounce_zone *bz;
 1095 
 1096         /* Check to see if we already have a suitable zone */
 1097         STAILQ_FOREACH(bz, &bounce_zone_list, links) {
 1098                 if ((dmat->common.alignment <= bz->alignment) &&
 1099                     (dmat->common.lowaddr >= bz->lowaddr)) {
 1100                         dmat->bounce_zone = bz;
 1101                         return (0);
 1102                 }
 1103         }
 1104 
 1105         if ((bz = (struct bounce_zone *)malloc(sizeof(*bz), M_DEVBUF,
 1106             M_NOWAIT | M_ZERO)) == NULL)
 1107                 return (ENOMEM);
 1108 
 1109         STAILQ_INIT(&bz->bounce_page_list);
 1110         bz->free_bpages = 0;
 1111         bz->reserved_bpages = 0;
 1112         bz->active_bpages = 0;
 1113         bz->lowaddr = dmat->common.lowaddr;
 1114         bz->alignment = MAX(dmat->common.alignment, PAGE_SIZE);
 1115         bz->map_count = 0;
 1116         snprintf(bz->zoneid, 8, "zone%d", busdma_zonecount);
 1117         busdma_zonecount++;
 1118         snprintf(bz->lowaddrid, 18, "%#jx", (uintmax_t)bz->lowaddr);
 1119         STAILQ_INSERT_TAIL(&bounce_zone_list, bz, links);
 1120         dmat->bounce_zone = bz;
 1121 
 1122         sysctl_ctx_init(&bz->sysctl_tree);
 1123         bz->sysctl_tree_top = SYSCTL_ADD_NODE(&bz->sysctl_tree,
 1124             SYSCTL_STATIC_CHILDREN(_hw_busdma), OID_AUTO, bz->zoneid,
 1125             CTLFLAG_RD, 0, "");
 1126         if (bz->sysctl_tree_top == NULL) {
 1127                 sysctl_ctx_free(&bz->sysctl_tree);
 1128                 return (0);     /* XXX error code? */
 1129         }
 1130 
 1131         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
 1132             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1133             "total_bpages", CTLFLAG_RD, &bz->total_bpages, 0,
 1134             "Total bounce pages");
 1135         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
 1136             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1137             "free_bpages", CTLFLAG_RD, &bz->free_bpages, 0,
 1138             "Free bounce pages");
 1139         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
 1140             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1141             "reserved_bpages", CTLFLAG_RD, &bz->reserved_bpages, 0,
 1142             "Reserved bounce pages");
 1143         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
 1144             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1145             "active_bpages", CTLFLAG_RD, &bz->active_bpages, 0,
 1146             "Active bounce pages");
 1147         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
 1148             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1149             "total_bounced", CTLFLAG_RD, &bz->total_bounced, 0,
 1150             "Total bounce requests");
 1151         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
 1152             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1153             "total_deferred", CTLFLAG_RD, &bz->total_deferred, 0,
 1154             "Total bounce requests that were deferred");
 1155         SYSCTL_ADD_STRING(busdma_sysctl_tree(bz),
 1156             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1157             "lowaddr", CTLFLAG_RD, bz->lowaddrid, 0, "");
 1158         SYSCTL_ADD_UAUTO(busdma_sysctl_tree(bz),
 1159             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1160             "alignment", CTLFLAG_RD, &bz->alignment, "");
 1161 
 1162         return (0);
 1163 }
 1164 
 1165 static int
 1166 alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages)
 1167 {
 1168         struct bounce_zone *bz;
 1169         int count;
 1170 
 1171         bz = dmat->bounce_zone;
 1172         count = 0;
 1173         while (numpages > 0) {
 1174                 struct bounce_page *bpage;
 1175 
 1176                 bpage = (struct bounce_page *)malloc(sizeof(*bpage), M_DEVBUF,
 1177                                                      M_NOWAIT | M_ZERO);
 1178 
 1179                 if (bpage == NULL)
 1180                         break;
 1181                 bpage->vaddr = (vm_offset_t)contigmalloc(PAGE_SIZE, M_DEVBUF,
 1182                     M_NOWAIT, 0ul, bz->lowaddr, PAGE_SIZE, 0);
 1183                 if (bpage->vaddr == 0) {
 1184                         free(bpage, M_DEVBUF);
 1185                         break;
 1186                 }
 1187                 bpage->busaddr = pmap_kextract(bpage->vaddr);
 1188                 mtx_lock(&bounce_lock);
 1189                 STAILQ_INSERT_TAIL(&bz->bounce_page_list, bpage, links);
 1190                 total_bpages++;
 1191                 bz->total_bpages++;
 1192                 bz->free_bpages++;
 1193                 mtx_unlock(&bounce_lock);
 1194                 count++;
 1195                 numpages--;
 1196         }
 1197         return (count);
 1198 }
 1199 
 1200 static int
 1201 reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int commit)
 1202 {
 1203         struct bounce_zone *bz;
 1204         int pages;
 1205 
 1206         mtx_assert(&bounce_lock, MA_OWNED);
 1207         bz = dmat->bounce_zone;
 1208         pages = MIN(bz->free_bpages, map->pagesneeded - map->pagesreserved);
 1209         if (commit == 0 && map->pagesneeded > (map->pagesreserved + pages))
 1210                 return (map->pagesneeded - (map->pagesreserved + pages));
 1211         bz->free_bpages -= pages;
 1212         bz->reserved_bpages += pages;
 1213         map->pagesreserved += pages;
 1214         pages = map->pagesneeded - map->pagesreserved;
 1215 
 1216         return (pages);
 1217 }
 1218 
 1219 static bus_addr_t
 1220 add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map, vm_offset_t vaddr,
 1221                 bus_addr_t addr, bus_size_t size)
 1222 {
 1223         struct bounce_zone *bz;
 1224         struct bounce_page *bpage;
 1225 
 1226         KASSERT(dmat->bounce_zone != NULL, ("no bounce zone in dma tag"));
 1227         KASSERT((map->flags & DMAMAP_COULD_BOUNCE) != 0,
 1228             ("add_bounce_page: bad map %p", map));
 1229 
 1230         bz = dmat->bounce_zone;
 1231         if (map->pagesneeded == 0)
 1232                 panic("add_bounce_page: map doesn't need any pages");
 1233         map->pagesneeded--;
 1234 
 1235         if (map->pagesreserved == 0)
 1236                 panic("add_bounce_page: map doesn't need any pages");
 1237         map->pagesreserved--;
 1238 
 1239         mtx_lock(&bounce_lock);
 1240         bpage = STAILQ_FIRST(&bz->bounce_page_list);
 1241         if (bpage == NULL)
 1242                 panic("add_bounce_page: free page list is empty");
 1243 
 1244         STAILQ_REMOVE_HEAD(&bz->bounce_page_list, links);
 1245         bz->reserved_bpages--;
 1246         bz->active_bpages++;
 1247         mtx_unlock(&bounce_lock);
 1248 
 1249         if (dmat->common.flags & BUS_DMA_KEEP_PG_OFFSET) {
 1250                 /* Page offset needs to be preserved. */
 1251                 bpage->vaddr |= addr & PAGE_MASK;
 1252                 bpage->busaddr |= addr & PAGE_MASK;
 1253         }
 1254         bpage->datavaddr = vaddr;
 1255         bpage->datapage = PHYS_TO_VM_PAGE(addr);
 1256         bpage->dataoffs = addr & PAGE_MASK;
 1257         bpage->datacount = size;
 1258         STAILQ_INSERT_TAIL(&(map->bpages), bpage, links);
 1259         return (bpage->busaddr);
 1260 }
 1261 
 1262 static void
 1263 free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage)
 1264 {
 1265         struct bus_dmamap *map;
 1266         struct bounce_zone *bz;
 1267 
 1268         bz = dmat->bounce_zone;
 1269         bpage->datavaddr = 0;
 1270         bpage->datacount = 0;
 1271         if (dmat->common.flags & BUS_DMA_KEEP_PG_OFFSET) {
 1272                 /*
 1273                  * Reset the bounce page to start at offset 0.  Other uses
 1274                  * of this bounce page may need to store a full page of
 1275                  * data and/or assume it starts on a page boundary.
 1276                  */
 1277                 bpage->vaddr &= ~PAGE_MASK;
 1278                 bpage->busaddr &= ~PAGE_MASK;
 1279         }
 1280 
 1281         mtx_lock(&bounce_lock);
 1282         STAILQ_INSERT_HEAD(&bz->bounce_page_list, bpage, links);
 1283         bz->free_bpages++;
 1284         bz->active_bpages--;
 1285         if ((map = STAILQ_FIRST(&bounce_map_waitinglist)) != NULL) {
 1286                 if (reserve_bounce_pages(map->dmat, map, 1) == 0) {
 1287                         STAILQ_REMOVE_HEAD(&bounce_map_waitinglist, links);
 1288                         STAILQ_INSERT_TAIL(&bounce_map_callbacklist,
 1289                             map, links);
 1290                         busdma_swi_pending = 1;
 1291                         bz->total_deferred++;
 1292                         swi_sched(vm_ih, 0);
 1293                 }
 1294         }
 1295         mtx_unlock(&bounce_lock);
 1296 }
 1297 
 1298 void
 1299 busdma_swi(void)
 1300 {
 1301         bus_dma_tag_t dmat;
 1302         struct bus_dmamap *map;
 1303 
 1304         mtx_lock(&bounce_lock);
 1305         while ((map = STAILQ_FIRST(&bounce_map_callbacklist)) != NULL) {
 1306                 STAILQ_REMOVE_HEAD(&bounce_map_callbacklist, links);
 1307                 mtx_unlock(&bounce_lock);
 1308                 dmat = map->dmat;
 1309                 (dmat->common.lockfunc)(dmat->common.lockfuncarg, BUS_DMA_LOCK);
 1310                 bus_dmamap_load_mem(map->dmat, map, &map->mem,
 1311                     map->callback, map->callback_arg, BUS_DMA_WAITOK);
 1312                 (dmat->common.lockfunc)(dmat->common.lockfuncarg,
 1313                     BUS_DMA_UNLOCK);
 1314                 mtx_lock(&bounce_lock);
 1315         }
 1316         mtx_unlock(&bounce_lock);
 1317 }
 1318 
 1319 struct bus_dma_impl bus_dma_bounce_impl = {
 1320         .tag_create = bounce_bus_dma_tag_create,
 1321         .tag_destroy = bounce_bus_dma_tag_destroy,
 1322         .map_create = bounce_bus_dmamap_create,
 1323         .map_destroy = bounce_bus_dmamap_destroy,
 1324         .mem_alloc = bounce_bus_dmamem_alloc,
 1325         .mem_free = bounce_bus_dmamem_free,
 1326         .load_phys = bounce_bus_dmamap_load_phys,
 1327         .load_buffer = bounce_bus_dmamap_load_buffer,
 1328         .load_ma = bus_dmamap_load_ma_triv,
 1329         .map_waitok = bounce_bus_dmamap_waitok,
 1330         .map_complete = bounce_bus_dmamap_complete,
 1331         .map_unload = bounce_bus_dmamap_unload,
 1332         .map_sync = bounce_bus_dmamap_sync
 1333 };

Cache object: 8bcaf106a0cfed8b518f0ea579431637


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