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