From d519b8a7d9f8e334618d97b324744afd8f97eee1 Mon Sep 17 00:00:00 2001 From: Roger Pau Monne Date: Wed, 30 Mar 2016 11:08:01 +0200 Subject: [PATCH] busdma/bounce: force bouncing of full buffer --- sys/x86/x86/busdma_bounce.c | 58 ++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/sys/x86/x86/busdma_bounce.c b/sys/x86/x86/busdma_bounce.c index 6e9aa70..b6a9da3 100644 --- a/sys/x86/x86/busdma_bounce.c +++ b/sys/x86/x86/busdma_bounce.c @@ -466,21 +466,31 @@ _bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map, vm_paddr_t buf, { bus_addr_t curaddr; bus_size_t sgsize; + bus_size_t len; + bool bounce; if ((map != &nobounce_dmamap && map->pagesneeded == 0)) { /* * Count the number of bounce pages * needed in order to complete this transfer */ + bounce = FALSE; +again: curaddr = buf; - while (buflen != 0) { - sgsize = MIN(buflen, dmat->common.maxsegsz); - if (bus_dma_run_filter(&dmat->common, curaddr)) { + len = buflen; + while (len != 0) { + sgsize = MIN(len, dmat->common.maxsegsz); + if (bounce || + bus_dma_run_filter(&dmat->common, curaddr)) { + if (!bounce) { + bounce = TRUE; + goto again; + } sgsize = MIN(PAGE_SIZE, sgsize); map->pagesneeded++; } curaddr += sgsize; - buflen -= sgsize; + len -= sgsize; } CTR1(KTR_BUSDMA, "pagesneeded= %d\n", map->pagesneeded); } @@ -494,6 +504,7 @@ _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map, pmap_t pmap, vm_offset_t vendaddr; bus_addr_t paddr; bus_size_t sg_len; + bool bounce; if ((map != &nobounce_dmamap && map->pagesneeded == 0)) { CTR4(KTR_BUSDMA, "lowaddr= %d Maxmem= %d, boundary= %d, " @@ -506,6 +517,8 @@ _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map, pmap_t pmap, * Count the number of bounce pages * needed in order to complete this transfer */ + bounce = FALSE; +again: vaddr = (vm_offset_t)buf; vendaddr = (vm_offset_t)buf + buflen; @@ -515,7 +528,12 @@ _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map, pmap_t pmap, paddr = pmap_kextract(vaddr); else paddr = pmap_extract(pmap, vaddr); - if (bus_dma_run_filter(&dmat->common, paddr) != 0) { + if (bounce || + bus_dma_run_filter(&dmat->common, paddr) != 0) { + if (!bounce) { + bounce = TRUE; + goto again; + } sg_len = PAGE_SIZE; map->pagesneeded++; } @@ -529,9 +547,10 @@ static void _bus_dmamap_count_ma(bus_dma_tag_t dmat, bus_dmamap_t map, struct vm_page **ma, int ma_offs, bus_size_t buflen, int flags) { - bus_size_t sg_len, max_sgsize; + bus_size_t sg_len, max_sgsize, len; int page_index; vm_paddr_t paddr; + bool bounce; if ((map != &nobounce_dmamap && map->pagesneeded == 0)) { CTR4(KTR_BUSDMA, "lowaddr= %d Maxmem= %d, boundary= %d, " @@ -545,13 +564,21 @@ _bus_dmamap_count_ma(bus_dma_tag_t dmat, bus_dmamap_t map, struct vm_page **ma, * Count the number of bounce pages * needed in order to complete this transfer */ + bounce = FALSE; +again: + len = buflen; page_index = 0; - while (buflen > 0) { + while (len > 0) { paddr = VM_PAGE_TO_PHYS(ma[page_index]) + ma_offs; sg_len = PAGE_SIZE - ma_offs; - max_sgsize = MIN(buflen, dmat->common.maxsegsz); + max_sgsize = MIN(len, dmat->common.maxsegsz); sg_len = MIN(sg_len, max_sgsize); - if (bus_dma_run_filter(&dmat->common, paddr) != 0) { + if (bounce || + bus_dma_run_filter(&dmat->common, paddr) != 0) { + if (!bounce) { + bounce = TRUE; + goto again; + } sg_len = MIN(PAGE_SIZE, max_sgsize); KASSERT((sg_len & (dmat->common.alignment - 1)) == 0, ("Segment size is not aligned")); @@ -560,9 +587,9 @@ _bus_dmamap_count_ma(bus_dma_tag_t dmat, bus_dmamap_t map, struct vm_page **ma, if (((ma_offs + sg_len) & ~PAGE_MASK) != 0) page_index++; ma_offs = (ma_offs + sg_len) & PAGE_MASK; - KASSERT(buflen >= sg_len, + KASSERT(len >= sg_len, ("Segment length overruns original buffer")); - buflen -= sg_len; + len -= sg_len; } CTR1(KTR_BUSDMA, "pagesneeded= %d\n", map->pagesneeded); } @@ -670,8 +697,7 @@ bounce_bus_dmamap_load_phys(bus_dma_tag_t dmat, bus_dmamap_t map, curaddr = buf; sgsize = MIN(buflen, dmat->common.maxsegsz); if (((dmat->bounce_flags & BUS_DMA_COULD_BOUNCE) != 0) && - map->pagesneeded != 0 && - bus_dma_run_filter(&dmat->common, curaddr)) { + map->pagesneeded != 0) { nextaddr = 0; sgsize = MIN(PAGE_SIZE, sgsize); if ((curaddr & PAGE_MASK) + sgsize > PAGE_SIZE) @@ -741,8 +767,7 @@ bounce_bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, max_sgsize = MIN(buflen, dmat->common.maxsegsz); sgsize = PAGE_SIZE - (curaddr & PAGE_MASK); if (((dmat->bounce_flags & BUS_DMA_COULD_BOUNCE) != 0) && - map->pagesneeded != 0 && - bus_dma_run_filter(&dmat->common, curaddr)) { + map->pagesneeded != 0) { sgsize = MIN(PAGE_SIZE, max_sgsize); curaddr = add_bounce_page(dmat, map, kvaddr, curaddr, 0, sgsize); @@ -796,8 +821,7 @@ bounce_bus_dmamap_load_ma(bus_dma_tag_t dmat, bus_dmamap_t map, max_sgsize = MIN(buflen, dmat->common.maxsegsz); sgsize = PAGE_SIZE - ma_offs; if (((dmat->bounce_flags & BUS_DMA_COULD_BOUNCE) != 0) && - map->pagesneeded != 0 && - bus_dma_run_filter(&dmat->common, paddr)) { + map->pagesneeded != 0) { sgsize = MIN(PAGE_SIZE, max_sgsize); /* * Check if two pages of the user provided buffer -- 2.6.4 (Apple Git-63)