Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F167462168
D58627.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
D58627.diff
View Options
diff --git a/sys/powerpc/powerpc/busdma_bounce.c b/sys/powerpc/powerpc/busdma_bounce.c
--- a/sys/powerpc/powerpc/busdma_bounce.c
+++ b/sys/powerpc/powerpc/busdma_bounce.c
@@ -115,17 +115,34 @@
#include "../../kern/subr_busdma_bounce.c"
/*
- * Returns true if the address falls within the tag's exclusion window, or
- * fails to meet its alignment requirements.
+ * Returns true if this page needs bouncing.
+ *
+ * A page needs bouncing if either:
+ * (1) its physical address is outside the tag's allowed range, or
+ * (2) it starts a new segment (i.e., paddr is not the direct continuation
+ * of the previous segment's end) and doesn't satisfy the tag's
+ * alignment constraint on that new segment's start.
+ *
+ * `seg_end` is the expected physical address of the byte immediately after
+ * the previous segment (i.e., prev_curaddr + prev_sgsize as passed to
+ * _bus_dmamap_addseg). Callers walking a buffer page-by-page should pass
+ * ~(bus_addr_t)0 for the first page (no previous segment) and update
+ * seg_end after each iteration from the actual curaddr they added to the
+ * segment list (source paddr if not bounced, bounce paddr if bounced).
+ * A page whose paddr matches seg_end continues the previous segment via
+ * addseg's coalescer and inherits alignment from the segment head; only
+ * physically-discontiguous pages start new segments requiring alignment
+ * re-check.
*/
static __inline bool
-must_bounce(bus_dma_tag_t dmat, bus_addr_t paddr)
+must_bounce(bus_dma_tag_t dmat, bus_addr_t paddr, bus_addr_t seg_end)
{
if (dmat->iommu == NULL && paddr > dmat->common.lowaddr &&
paddr <= dmat->common.highaddr)
return (true);
- if (!vm_addr_align_ok(paddr, dmat->common.alignment))
+ if (paddr != seg_end &&
+ !vm_addr_align_ok(paddr, dmat->common.alignment))
return (true);
return (false);
@@ -476,12 +493,24 @@
* needed in order to complete this transfer
*/
curaddr = buf;
+ /*
+ * Track the expected end of the previous segment so we mirror
+ * load_phys()'s decisions and don't under-count bounces. After
+ * a "would bounce" page, load will have replaced curaddr with a
+ * bounce paddr that won't match the next source paddr; count
+ * simulates that by resetting seg_end to 0 (which no source
+ * paddr can match on modern PPC where page 0 is reserved).
+ */
+ bus_addr_t seg_end = ~(bus_addr_t)0;
while (buflen != 0) {
sgsize = buflen;
- if (must_bounce(dmat, curaddr)) {
+ if (must_bounce(dmat, curaddr, seg_end)) {
sgsize = MIN(sgsize,
PAGE_SIZE - (curaddr & PAGE_MASK));
map->pagesneeded++;
+ seg_end = 0;
+ } else {
+ seg_end = curaddr + sgsize;
}
curaddr += sgsize;
buflen -= sgsize;
@@ -509,6 +538,7 @@
*/
vaddr = (vm_offset_t)buf;
vendaddr = (vm_offset_t)buf + buflen;
+ bus_addr_t seg_end = ~(bus_addr_t)0;
while (vaddr < vendaddr) {
bus_size_t sg_len;
@@ -519,9 +549,12 @@
paddr = pmap_kextract(vaddr);
else
paddr = pmap_extract(pmap, vaddr);
- if (must_bounce(dmat, paddr)) {
+ if (must_bounce(dmat, paddr, seg_end)) {
sg_len = roundup2(sg_len, dmat->common.alignment);
map->pagesneeded++;
+ seg_end = 0;
+ } else {
+ seg_end = paddr + sg_len;
}
vaddr += sg_len;
}
@@ -557,10 +590,12 @@
}
}
+ bus_addr_t seg_end = ~(bus_addr_t)0;
while (buflen > 0) {
curaddr = buf;
sgsize = buflen;
- if (map->pagesneeded != 0 && must_bounce(dmat, curaddr)) {
+ if (map->pagesneeded != 0 &&
+ must_bounce(dmat, curaddr, seg_end)) {
sgsize = MIN(sgsize, PAGE_SIZE - (curaddr & PAGE_MASK));
curaddr = add_bounce_page(dmat, map, 0, curaddr,
sgsize);
@@ -568,6 +603,13 @@
if (!_bus_dmamap_addsegs(dmat, map, curaddr, sgsize, segs,
segp))
break;
+ /*
+ * Track the added segment's end so must_bounce() on the next
+ * iteration correctly identifies whether we're continuing a
+ * segment (source contiguous with previous curaddr, which may
+ * itself be a bounce paddr) or starting a new one.
+ */
+ seg_end = curaddr + sgsize;
buf += sgsize;
buflen -= sgsize;
}
@@ -619,6 +661,7 @@
}
vaddr = buf;
+ bus_addr_t seg_end = ~(bus_addr_t)0;
while (buflen > 0) {
/*
@@ -636,7 +679,8 @@
* Compute the segment size, and adjust counts.
*/
sgsize = MIN(buflen, PAGE_SIZE - (curaddr & PAGE_MASK));
- if (map->pagesneeded != 0 && must_bounce(dmat, curaddr)) {
+ if (map->pagesneeded != 0 &&
+ must_bounce(dmat, curaddr, seg_end)) {
sgsize = roundup2(sgsize, dmat->common.alignment);
sgsize = MIN(sgsize, buflen);
curaddr = add_bounce_page(dmat, map, kvaddr, curaddr,
@@ -646,6 +690,13 @@
if (!_bus_dmamap_addsegs(dmat, map, curaddr, sgsize, segs,
segp))
break;
+ /*
+ * Track the added segment's end so must_bounce() on the next
+ * iteration correctly identifies whether we're continuing a
+ * segment (source contiguous with previous curaddr, which may
+ * itself be a bounce paddr) or starting a new one.
+ */
+ seg_end = curaddr + sgsize;
vaddr += sgsize;
buflen -= MIN(sgsize, buflen); /* avoid underflow */
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Aug 22, 10:23 PM (3 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35972926
Default Alt Text
D58627.diff (5 KB)
Attached To
Mode
D58627: powerpc/dma: Fix accounting and loading large segments
Attached
Detach File
Event Timeline
Log In to Comment