busdma_machdep.c revision 117691
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: head/sys/i386/i386/busdma_machdep.c 117691 2003-07-17 16:07:46Z scottl $"); 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/lock.h> 37#include <sys/proc.h> 38#include <sys/mutex.h> 39#include <sys/mbuf.h> 40#include <sys/uio.h> 41 42#include <vm/vm.h> 43#include <vm/vm_page.h> 44#include <vm/vm_map.h> 45 46#include <machine/atomic.h> 47#include <machine/bus.h> 48#include <machine/md_var.h> 49 50#define MAX_BPAGES 512 51 52struct bus_dma_tag { 53 bus_dma_tag_t parent; 54 bus_size_t alignment; 55 bus_size_t boundary; 56 bus_addr_t lowaddr; 57 bus_addr_t highaddr; 58 bus_dma_filter_t *filter; 59 void *filterarg; 60 bus_size_t maxsize; 61 u_int nsegments; 62 bus_size_t maxsegsz; 63 int flags; 64 int ref_count; 65 int map_count; 66 bus_dma_lock_t *lockfunc; 67 void *lockfuncarg; 68}; 69 70struct bounce_page { 71 vm_offset_t vaddr; /* kva of bounce buffer */ 72 bus_addr_t busaddr; /* Physical address */ 73 vm_offset_t datavaddr; /* kva of client data */ 74 bus_size_t datacount; /* client data count */ 75 STAILQ_ENTRY(bounce_page) links; 76}; 77 78int busdma_swi_pending; 79 80static struct mtx bounce_lock; 81static STAILQ_HEAD(bp_list, bounce_page) bounce_page_list; 82static int free_bpages; 83static int reserved_bpages; 84static int active_bpages; 85static int total_bpages; 86static bus_addr_t bounce_lowaddr = BUS_SPACE_MAXADDR; 87 88struct bus_dmamap { 89 struct bp_list bpages; 90 int pagesneeded; 91 int pagesreserved; 92 bus_dma_tag_t dmat; 93 void *buf; /* unmapped buffer pointer */ 94 bus_size_t buflen; /* unmapped buffer length */ 95 bus_dmamap_callback_t *callback; 96 void *callback_arg; 97 STAILQ_ENTRY(bus_dmamap) links; 98}; 99 100static STAILQ_HEAD(, bus_dmamap) bounce_map_waitinglist; 101static STAILQ_HEAD(, bus_dmamap) bounce_map_callbacklist; 102static struct bus_dmamap nobounce_dmamap; 103 104static void init_bounce_pages(void *dummy); 105static int alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages); 106static int reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map, 107 int commit); 108static bus_addr_t add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map, 109 vm_offset_t vaddr, bus_size_t size); 110static void free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage); 111static __inline int run_filter(bus_dma_tag_t dmat, bus_addr_t paddr); 112 113/* 114 * Return true if a match is made. 115 * 116 * To find a match walk the chain of bus_dma_tag_t's looking for 'paddr'. 117 * 118 * If paddr is within the bounds of the dma tag then call the filter callback 119 * to check for a match, if there is no filter callback then assume a match. 120 */ 121static __inline int 122run_filter(bus_dma_tag_t dmat, bus_addr_t paddr) 123{ 124 int retval; 125 126 retval = 0; 127 do { 128 if (paddr > dmat->lowaddr 129 && paddr <= dmat->highaddr 130 && (dmat->filter == NULL 131 || (*dmat->filter)(dmat->filterarg, paddr) != 0)) 132 retval = 1; 133 134 dmat = dmat->parent; 135 } while (retval == 0 && dmat != NULL); 136 return (retval); 137} 138 139/* 140 * Convenience function for manipulating driver locks from busdma (during 141 * busdma_swi, for example). Drivers that don't provide their own locks 142 * should specify &Giant to dmat->lockfuncarg. Drivers that use their own 143 * non-mutex locking scheme don't have to use this at all. 144 */ 145void 146busdma_lock_mutex(void *arg, bus_dma_lock_op_t op) 147{ 148 struct mtx *dmtx; 149 150 dmtx = (struct mtx *)arg; 151 switch (op) { 152 case BUS_DMA_LOCK: 153 mtx_lock(dmtx); 154 break; 155 case BUS_DMA_UNLOCK: 156 mtx_unlock(dmtx); 157 break; 158 default: 159 panic("Unknown operation 0x%x for busdma_lock_mutex!", op); 160 } 161} 162 163/* 164 * dflt_lock should never get called. It gets put into the dma tag when 165 * lockfunc == NULL, which is only valid if the maps that are associated 166 * with the tag are meant to never be defered. 167 * XXX Should have a way to identify which driver is responsible here. 168 */ 169static void 170dflt_lock(void *arg, bus_dma_lock_op_t op) 171{ 172 panic("driver error: busdma dflt_lock called"); 173} 174 175#define BUS_DMA_MIN_ALLOC_COMP BUS_DMA_BUS4 176/* 177 * Allocate a device specific dma_tag. 178 */ 179int 180bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, 181 bus_size_t boundary, bus_addr_t lowaddr, 182 bus_addr_t highaddr, bus_dma_filter_t *filter, 183 void *filterarg, bus_size_t maxsize, int nsegments, 184 bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc, 185 void *lockfuncarg, bus_dma_tag_t *dmat) 186{ 187 bus_dma_tag_t newtag; 188 int error = 0; 189 190 /* Return a NULL tag on failure */ 191 *dmat = NULL; 192 193 newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_DEVBUF, M_NOWAIT); 194 if (newtag == NULL) 195 return (ENOMEM); 196 197 newtag->parent = parent; 198 newtag->alignment = alignment; 199 newtag->boundary = boundary; 200 newtag->lowaddr = trunc_page((vm_paddr_t)lowaddr) + (PAGE_SIZE - 1); 201 newtag->highaddr = trunc_page((vm_paddr_t)highaddr) + 202 (PAGE_SIZE - 1); 203 newtag->filter = filter; 204 newtag->filterarg = filterarg; 205 newtag->maxsize = maxsize; 206 newtag->nsegments = nsegments; 207 newtag->maxsegsz = maxsegsz; 208 newtag->flags = flags; 209 newtag->ref_count = 1; /* Count ourself */ 210 newtag->map_count = 0; 211 if (lockfunc != NULL) { 212 newtag->lockfunc = lockfunc; 213 newtag->lockfuncarg = lockfuncarg; 214 } else { 215 newtag->lockfunc = dflt_lock; 216 newtag->lockfuncarg = NULL; 217 } 218 219 /* Take into account any restrictions imposed by our parent tag */ 220 if (parent != NULL) { 221 newtag->lowaddr = MIN(parent->lowaddr, newtag->lowaddr); 222 newtag->highaddr = MAX(parent->highaddr, newtag->highaddr); 223 /* 224 * XXX Not really correct??? Probably need to honor boundary 225 * all the way up the inheritence chain. 226 */ 227 newtag->boundary = MAX(parent->boundary, newtag->boundary); 228 if (newtag->filter == NULL) { 229 /* 230 * Short circuit looking at our parent directly 231 * since we have encapsulated all of its information 232 */ 233 newtag->filter = parent->filter; 234 newtag->filterarg = parent->filterarg; 235 newtag->parent = parent->parent; 236 } 237 if (newtag->parent != NULL) 238 atomic_add_int(&parent->ref_count, 1); 239 } 240 241 if (newtag->lowaddr < ptoa((vm_paddr_t)Maxmem) && 242 (flags & BUS_DMA_ALLOCNOW) != 0) { 243 /* Must bounce */ 244 245 if (lowaddr > bounce_lowaddr) { 246 /* 247 * Go through the pool and kill any pages 248 * that don't reside below lowaddr. 249 */ 250 panic("bus_dma_tag_create: page reallocation " 251 "not implemented"); 252 } 253 if (ptoa(total_bpages) < maxsize) { 254 int pages; 255 256 pages = atop(maxsize) - total_bpages; 257 258 /* Add pages to our bounce pool */ 259 if (alloc_bounce_pages(newtag, pages) < pages) 260 error = ENOMEM; 261 } 262 /* Performed initial allocation */ 263 newtag->flags |= BUS_DMA_MIN_ALLOC_COMP; 264 } 265 266 if (error != 0) { 267 free(newtag, M_DEVBUF); 268 } else { 269 *dmat = newtag; 270 } 271 return (error); 272} 273 274int 275bus_dma_tag_destroy(bus_dma_tag_t dmat) 276{ 277 if (dmat != NULL) { 278 279 if (dmat->map_count != 0) 280 return (EBUSY); 281 282 while (dmat != NULL) { 283 bus_dma_tag_t parent; 284 285 parent = dmat->parent; 286 atomic_subtract_int(&dmat->ref_count, 1); 287 if (dmat->ref_count == 0) { 288 free(dmat, M_DEVBUF); 289 /* 290 * Last reference count, so 291 * release our reference 292 * count on our parent. 293 */ 294 dmat = parent; 295 } else 296 dmat = NULL; 297 } 298 } 299 return (0); 300} 301 302/* 303 * Allocate a handle for mapping from kva/uva/physical 304 * address space into bus device space. 305 */ 306int 307bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp) 308{ 309 int error; 310 311 error = 0; 312 313 if (dmat->lowaddr < ptoa((vm_paddr_t)Maxmem)) { 314 /* Must bounce */ 315 int maxpages; 316 317 *mapp = (bus_dmamap_t)malloc(sizeof(**mapp), M_DEVBUF, 318 M_NOWAIT | M_ZERO); 319 if (*mapp == NULL) 320 return (ENOMEM); 321 322 /* Initialize the new map */ 323 STAILQ_INIT(&((*mapp)->bpages)); 324 325 /* 326 * Attempt to add pages to our pool on a per-instance 327 * basis up to a sane limit. 328 */ 329 maxpages = MIN(MAX_BPAGES, Maxmem - atop(dmat->lowaddr)); 330 if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0 331 || (dmat->map_count > 0 332 && total_bpages < maxpages)) { 333 int pages; 334 335 if (dmat->lowaddr > bounce_lowaddr) { 336 /* 337 * Go through the pool and kill any pages 338 * that don't reside below lowaddr. 339 */ 340 panic("bus_dmamap_create: page reallocation " 341 "not implemented"); 342 } 343 pages = MAX(atop(dmat->maxsize), 1); 344 pages = MIN(maxpages - total_bpages, pages); 345 if (alloc_bounce_pages(dmat, pages) < pages) 346 error = ENOMEM; 347 348 if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0) { 349 if (error == 0) 350 dmat->flags |= BUS_DMA_MIN_ALLOC_COMP; 351 } else { 352 error = 0; 353 } 354 } 355 } else { 356 *mapp = NULL; 357 } 358 if (error == 0) 359 dmat->map_count++; 360 return (error); 361} 362 363/* 364 * Destroy a handle for mapping from kva/uva/physical 365 * address space into bus device space. 366 */ 367int 368bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map) 369{ 370 if (map != NULL && map != &nobounce_dmamap) { 371 if (STAILQ_FIRST(&map->bpages) != NULL) 372 return (EBUSY); 373 free(map, M_DEVBUF); 374 } 375 dmat->map_count--; 376 return (0); 377} 378 379 380/* 381 * Allocate a piece of memory that can be efficiently mapped into 382 * bus device space based on the constraints lited in the dma tag. 383 * A dmamap to for use with dmamap_load is also allocated. 384 */ 385int 386bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, 387 bus_dmamap_t *mapp) 388{ 389 /* If we succeed, no mapping/bouncing will be required */ 390 *mapp = NULL; 391 392 if ((dmat->maxsize <= PAGE_SIZE) && 393 dmat->lowaddr >= ptoa((vm_paddr_t)Maxmem)) { 394 *vaddr = malloc(dmat->maxsize, M_DEVBUF, 395 (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK); 396 } else { 397 /* 398 * XXX Use Contigmalloc until it is merged into this facility 399 * and handles multi-seg allocations. Nobody is doing 400 * multi-seg allocations yet though. 401 */ 402 mtx_lock(&Giant); 403 *vaddr = contigmalloc(dmat->maxsize, M_DEVBUF, 404 (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK, 405 0ul, dmat->lowaddr, dmat->alignment? dmat->alignment : 1ul, 406 dmat->boundary); 407 mtx_unlock(&Giant); 408 } 409 if (*vaddr == NULL) 410 return (ENOMEM); 411 return (0); 412} 413 414/* 415 * Free a piece of memory and it's allociated dmamap, that was allocated 416 * via bus_dmamem_alloc. Make the same choice for free/contigfree. 417 */ 418void 419bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map) 420{ 421 /* 422 * dmamem does not need to be bounced, so the map should be 423 * NULL 424 */ 425 if (map != NULL) 426 panic("bus_dmamem_free: Invalid map freed\n"); 427 if ((dmat->maxsize <= PAGE_SIZE) 428 && dmat->lowaddr >= ptoa((vm_paddr_t)Maxmem)) 429 free(vaddr, M_DEVBUF); 430 else { 431 mtx_lock(&Giant); 432 contigfree(vaddr, dmat->maxsize, M_DEVBUF); 433 mtx_unlock(&Giant); 434 } 435} 436 437/* 438 * Utility function to load a linear buffer. lastaddrp holds state 439 * between invocations (for multiple-buffer loads). segp contains 440 * the starting segment on entrace, and the ending segment on exit. 441 * first indicates if this is the first invocation of this function. 442 */ 443static int 444_bus_dmamap_load_buffer(bus_dma_tag_t dmat, 445 bus_dmamap_t map, 446 bus_dma_segment_t segs[], 447 void *buf, bus_size_t buflen, 448 struct thread *td, 449 int flags, 450 bus_addr_t *lastaddrp, 451 int *segp, 452 int first) 453{ 454 bus_size_t sgsize; 455 bus_addr_t curaddr, lastaddr, baddr, bmask; 456 vm_offset_t vaddr; 457 bus_addr_t paddr; 458 int needbounce = 0; 459 int seg; 460 pmap_t pmap; 461 462 if (map == NULL) 463 map = &nobounce_dmamap; 464 465 if (td != NULL) 466 pmap = vmspace_pmap(td->td_proc->p_vmspace); 467 else 468 pmap = NULL; 469 470 if (dmat->lowaddr < ptoa((vm_paddr_t)Maxmem)) { 471 vm_offset_t vendaddr; 472 473 /* 474 * Count the number of bounce pages 475 * needed in order to complete this transfer 476 */ 477 vaddr = trunc_page((vm_offset_t)buf); 478 vendaddr = (vm_offset_t)buf + buflen; 479 480 while (vaddr < vendaddr) { 481 paddr = pmap_kextract(vaddr); 482 if (run_filter(dmat, paddr) != 0) { 483 needbounce = 1; 484 map->pagesneeded++; 485 } 486 vaddr += PAGE_SIZE; 487 } 488 } 489 490 vaddr = (vm_offset_t)buf; 491 492 /* Reserve Necessary Bounce Pages */ 493 if (map->pagesneeded != 0) { 494 mtx_lock(&bounce_lock); 495 if (flags & BUS_DMA_NOWAIT) { 496 if (reserve_bounce_pages(dmat, map, 0) != 0) { 497 mtx_unlock(&bounce_lock); 498 return (ENOMEM); 499 } 500 } else { 501 if (reserve_bounce_pages(dmat, map, 1) != 0) { 502 /* Queue us for resources */ 503 map->dmat = dmat; 504 map->buf = buf; 505 map->buflen = buflen; 506 STAILQ_INSERT_TAIL(&bounce_map_waitinglist, 507 map, links); 508 mtx_unlock(&bounce_lock); 509 return (EINPROGRESS); 510 } 511 } 512 mtx_unlock(&bounce_lock); 513 } 514 515 lastaddr = *lastaddrp; 516 bmask = ~(dmat->boundary - 1); 517 518 for (seg = *segp; buflen > 0 ; ) { 519 /* 520 * Get the physical address for this segment. 521 */ 522 if (pmap) 523 curaddr = pmap_extract(pmap, vaddr); 524 else 525 curaddr = pmap_kextract(vaddr); 526 527 /* 528 * Compute the segment size, and adjust counts. 529 */ 530 sgsize = PAGE_SIZE - ((u_long)curaddr & PAGE_MASK); 531 if (buflen < sgsize) 532 sgsize = buflen; 533 534 /* 535 * Make sure we don't cross any boundaries. 536 */ 537 if (dmat->boundary > 0) { 538 baddr = (curaddr + dmat->boundary) & bmask; 539 if (sgsize > (baddr - curaddr)) 540 sgsize = (baddr - curaddr); 541 } 542 543 if (map->pagesneeded != 0 && run_filter(dmat, curaddr)) 544 curaddr = add_bounce_page(dmat, map, vaddr, sgsize); 545 546 /* 547 * Insert chunk into a segment, coalescing with 548 * previous segment if possible. 549 */ 550 if (first) { 551 segs[seg].ds_addr = curaddr; 552 segs[seg].ds_len = sgsize; 553 first = 0; 554 } else { 555 if (needbounce == 0 && curaddr == lastaddr && 556 (segs[seg].ds_len + sgsize) <= dmat->maxsegsz && 557 (dmat->boundary == 0 || 558 (segs[seg].ds_addr & bmask) == (curaddr & bmask))) 559 segs[seg].ds_len += sgsize; 560 else { 561 if (++seg >= dmat->nsegments) 562 break; 563 segs[seg].ds_addr = curaddr; 564 segs[seg].ds_len = sgsize; 565 } 566 } 567 568 lastaddr = curaddr + sgsize; 569 vaddr += sgsize; 570 buflen -= sgsize; 571 } 572 573 *segp = seg; 574 *lastaddrp = lastaddr; 575 576 /* 577 * Did we fit? 578 */ 579 return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */ 580} 581 582#define BUS_DMAMAP_NSEGS ((64 * 1024) / PAGE_SIZE + 1) 583 584/* 585 * Map the buffer buf into bus space using the dmamap map. 586 */ 587int 588bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, 589 bus_size_t buflen, bus_dmamap_callback_t *callback, 590 void *callback_arg, int flags) 591{ 592#ifdef __GNUC__ 593 bus_dma_segment_t dm_segments[dmat->nsegments]; 594#else 595 bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS]; 596#endif 597 bus_addr_t lastaddr = 0; 598 int error, nsegs = 0; 599 600 if (map != NULL) { 601 flags |= BUS_DMA_WAITOK; 602 map->callback = callback; 603 map->callback_arg = callback_arg; 604 } 605 606 error = _bus_dmamap_load_buffer(dmat, map, dm_segments, buf, buflen, 607 NULL, flags, &lastaddr, &nsegs, 1); 608 609 if (error == EINPROGRESS) 610 return (error); 611 612 if (error) 613 (*callback)(callback_arg, dm_segments, 0, error); 614 else 615 (*callback)(callback_arg, dm_segments, nsegs + 1, 0); 616 617 return (0); 618} 619 620 621/* 622 * Like _bus_dmamap_load(), but for mbufs. 623 */ 624int 625bus_dmamap_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map, 626 struct mbuf *m0, 627 bus_dmamap_callback2_t *callback, void *callback_arg, 628 int flags) 629{ 630#ifdef __GNUC__ 631 bus_dma_segment_t dm_segments[dmat->nsegments]; 632#else 633 bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS]; 634#endif 635 int nsegs, error; 636 637 M_ASSERTPKTHDR(m0); 638 639 flags |= BUS_DMA_NOWAIT; 640 nsegs = 0; 641 error = 0; 642 if (m0->m_pkthdr.len <= dmat->maxsize) { 643 int first = 1; 644 bus_addr_t lastaddr = 0; 645 struct mbuf *m; 646 647 for (m = m0; m != NULL && error == 0; m = m->m_next) { 648 if (m->m_len > 0) { 649 error = _bus_dmamap_load_buffer(dmat, map, 650 dm_segments, 651 m->m_data, m->m_len, 652 NULL, flags, &lastaddr, 653 &nsegs, first); 654 first = 0; 655 } 656 } 657 } else { 658 error = EINVAL; 659 } 660 661 if (error) { 662 /* force "no valid mappings" in callback */ 663 (*callback)(callback_arg, dm_segments, 0, 0, error); 664 } else { 665 (*callback)(callback_arg, dm_segments, 666 nsegs+1, m0->m_pkthdr.len, error); 667 } 668 return (error); 669} 670 671/* 672 * Like _bus_dmamap_load(), but for uios. 673 */ 674int 675bus_dmamap_load_uio(bus_dma_tag_t dmat, bus_dmamap_t map, 676 struct uio *uio, 677 bus_dmamap_callback2_t *callback, void *callback_arg, 678 int flags) 679{ 680 bus_addr_t lastaddr; 681#ifdef __GNUC__ 682 bus_dma_segment_t dm_segments[dmat->nsegments]; 683#else 684 bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS]; 685#endif 686 int nsegs, error, first, i; 687 bus_size_t resid; 688 struct iovec *iov; 689 struct thread *td = NULL; 690 691 flags |= BUS_DMA_NOWAIT; 692 resid = uio->uio_resid; 693 iov = uio->uio_iov; 694 695 if (uio->uio_segflg == UIO_USERSPACE) { 696 td = uio->uio_td; 697 KASSERT(td != NULL, 698 ("bus_dmamap_load_uio: USERSPACE but no proc")); 699 } 700 701 nsegs = 0; 702 error = 0; 703 first = 1; 704 for (i = 0; i < uio->uio_iovcnt && resid != 0 && !error; i++) { 705 /* 706 * Now at the first iovec to load. Load each iovec 707 * until we have exhausted the residual count. 708 */ 709 bus_size_t minlen = 710 resid < iov[i].iov_len ? resid : iov[i].iov_len; 711 caddr_t addr = (caddr_t) iov[i].iov_base; 712 713 if (minlen > 0) { 714 error = _bus_dmamap_load_buffer(dmat, map, 715 dm_segments, 716 addr, minlen, 717 td, flags, &lastaddr, &nsegs, first); 718 first = 0; 719 720 resid -= minlen; 721 } 722 } 723 724 if (error) { 725 /* force "no valid mappings" in callback */ 726 (*callback)(callback_arg, dm_segments, 0, 0, error); 727 } else { 728 (*callback)(callback_arg, dm_segments, 729 nsegs+1, uio->uio_resid, error); 730 } 731 return (error); 732} 733 734/* 735 * Release the mapping held by map. 736 */ 737void 738_bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map) 739{ 740 struct bounce_page *bpage; 741 742 while ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) { 743 STAILQ_REMOVE_HEAD(&map->bpages, links); 744 free_bounce_page(dmat, bpage); 745 } 746} 747 748void 749_bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op) 750{ 751 struct bounce_page *bpage; 752 753 if ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) { 754 /* 755 * Handle data bouncing. We might also 756 * want to add support for invalidating 757 * the caches on broken hardware 758 */ 759 if (op & BUS_DMASYNC_PREWRITE) { 760 while (bpage != NULL) { 761 bcopy((void *)bpage->datavaddr, 762 (void *)bpage->vaddr, 763 bpage->datacount); 764 bpage = STAILQ_NEXT(bpage, links); 765 } 766 } 767 768 if (op & BUS_DMASYNC_POSTREAD) { 769 while (bpage != NULL) { 770 bcopy((void *)bpage->vaddr, 771 (void *)bpage->datavaddr, 772 bpage->datacount); 773 bpage = STAILQ_NEXT(bpage, links); 774 } 775 } 776 } 777} 778 779static void 780init_bounce_pages(void *dummy __unused) 781{ 782 783 free_bpages = 0; 784 reserved_bpages = 0; 785 active_bpages = 0; 786 total_bpages = 0; 787 STAILQ_INIT(&bounce_page_list); 788 STAILQ_INIT(&bounce_map_waitinglist); 789 STAILQ_INIT(&bounce_map_callbacklist); 790 mtx_init(&bounce_lock, "bounce pages lock", NULL, MTX_DEF); 791} 792SYSINIT(bpages, SI_SUB_LOCK, SI_ORDER_ANY, init_bounce_pages, NULL); 793 794static int 795alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages) 796{ 797 int count; 798 799 count = 0; 800 while (numpages > 0) { 801 struct bounce_page *bpage; 802 803 bpage = (struct bounce_page *)malloc(sizeof(*bpage), M_DEVBUF, 804 M_NOWAIT | M_ZERO); 805 806 if (bpage == NULL) 807 break; 808 mtx_lock(&Giant); 809 bpage->vaddr = (vm_offset_t)contigmalloc(PAGE_SIZE, M_DEVBUF, 810 M_NOWAIT, 0ul, 811 dmat->lowaddr, 812 PAGE_SIZE, 813 dmat->boundary); 814 mtx_unlock(&Giant); 815 if (bpage->vaddr == 0) { 816 free(bpage, M_DEVBUF); 817 break; 818 } 819 bpage->busaddr = pmap_kextract(bpage->vaddr); 820 mtx_lock(&bounce_lock); 821 STAILQ_INSERT_TAIL(&bounce_page_list, bpage, links); 822 total_bpages++; 823 free_bpages++; 824 mtx_unlock(&bounce_lock); 825 count++; 826 numpages--; 827 } 828 return (count); 829} 830 831static int 832reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int commit) 833{ 834 int pages; 835 836 mtx_assert(&bounce_lock, MA_OWNED); 837 pages = MIN(free_bpages, map->pagesneeded - map->pagesreserved); 838 if (commit == 0 && map->pagesneeded > (map->pagesreserved + pages)) 839 return (map->pagesneeded - (map->pagesreserved + pages)); 840 free_bpages -= pages; 841 reserved_bpages += pages; 842 map->pagesreserved += pages; 843 pages = map->pagesneeded - map->pagesreserved; 844 845 return (pages); 846} 847 848static bus_addr_t 849add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map, vm_offset_t vaddr, 850 bus_size_t size) 851{ 852 struct bounce_page *bpage; 853 854 KASSERT(map != NULL && map != &nobounce_dmamap, 855 ("add_bounce_page: bad map %p", map)); 856 857 if (map->pagesneeded == 0) 858 panic("add_bounce_page: map doesn't need any pages"); 859 map->pagesneeded--; 860 861 if (map->pagesreserved == 0) 862 panic("add_bounce_page: map doesn't need any pages"); 863 map->pagesreserved--; 864 865 mtx_lock(&bounce_lock); 866 bpage = STAILQ_FIRST(&bounce_page_list); 867 if (bpage == NULL) 868 panic("add_bounce_page: free page list is empty"); 869 870 STAILQ_REMOVE_HEAD(&bounce_page_list, links); 871 reserved_bpages--; 872 active_bpages++; 873 mtx_unlock(&bounce_lock); 874 875 bpage->datavaddr = vaddr; 876 bpage->datacount = size; 877 STAILQ_INSERT_TAIL(&(map->bpages), bpage, links); 878 return (bpage->busaddr); 879} 880 881static void 882free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage) 883{ 884 struct bus_dmamap *map; 885 886 bpage->datavaddr = 0; 887 bpage->datacount = 0; 888 889 mtx_lock(&bounce_lock); 890 STAILQ_INSERT_HEAD(&bounce_page_list, bpage, links); 891 free_bpages++; 892 active_bpages--; 893 if ((map = STAILQ_FIRST(&bounce_map_waitinglist)) != NULL) { 894 if (reserve_bounce_pages(map->dmat, map, 1) == 0) { 895 STAILQ_REMOVE_HEAD(&bounce_map_waitinglist, links); 896 STAILQ_INSERT_TAIL(&bounce_map_callbacklist, 897 map, links); 898 busdma_swi_pending = 1; 899 swi_sched(vm_ih, 0); 900 } 901 } 902 mtx_unlock(&bounce_lock); 903} 904 905void 906busdma_swi(void) 907{ 908 bus_dma_tag_t dmat; 909 struct bus_dmamap *map; 910 911 mtx_lock(&bounce_lock); 912 while ((map = STAILQ_FIRST(&bounce_map_callbacklist)) != NULL) { 913 STAILQ_REMOVE_HEAD(&bounce_map_callbacklist, links); 914 mtx_unlock(&bounce_lock); 915 dmat = map->dmat; 916 (dmat->lockfunc)(dmat->lockfuncarg, BUS_DMA_LOCK); 917 bus_dmamap_load(map->dmat, map, map->buf, map->buflen, 918 map->callback, map->callback_arg, /*flags*/0); 919 (dmat->lockfunc)(dmat->lockfuncarg, BUS_DMA_UNLOCK); 920 mtx_lock(&bounce_lock); 921 } 922 mtx_unlock(&bounce_lock); 923} 924