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