Bug 93961

Summary: [busdma] Problem in bounce buffer handling in sys/amd64/amd64/busdma_machdep.c: _bus_dmamap_load_buffer()
Product: Base System Reporter: Kaustubh Patil <kpatil>
Component: amd64Assignee: freebsd-amd64 (Nobody) <amd64>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: Unspecified   
Hardware: Any   
OS: Any   

Description Kaustubh Patil 2006-03-01 01:00:20 UTC
A couple of us have observed this problem with 5.3-64 bit version on a high
memory machine (> 4GiB memory), but a look at the current code indicates
that the bug might be present in recent branches as well.

_bus_dmamap_load_buffer() first calculates the number of pages that need to be
bounced.

vaddr = trunc_page((vm_offset_t)buf);    ---------- (1)
vendaddr = (vm_offset_t)buf + buflen;

while (vaddr < vendaddr) {
   paddr = pmap_kextract(vaddr);         ---------- (2)
   if (run_filter(dmat, paddr) != 0) {   ---------- (3)
      needbounce = 1;
      map->pagesneeded++;                ---------- (4)
   }
   vaddr += PAGE_SIZE;
}

Consider a dma-map that is set up to indicate the need to bounce
o if the address is in high memory or
o if the address is not page-aligned.

(1) The above code uses trunc_page() on the input virtual address.
(2) This page-aligned address is passed to pmap_kextract(), which returns a
    page-aligned paddr ...
(3) ... which is passed to run_filter(). As the paddr is page-aligned,
    run_filter() finds it okay as far as the page-alignment requirement is
    concerned.
(4) The count obtained is stored in "map->pagesneeded".

Later the function starts allocating bounce pages. However unlike the above code
the allocation code does not align virtual addresses, so run_filter() call
catches the input paddr if it is not page-aligned, then a bounce page is
allocated for it using add_bounce_page() and "map->pagesneeded" is decremented.

Thus this code consumes "map->pagesneeded" for a virtual address that
was not initially identified for bouncing. This potentially leads to other needy
pages not being bounced.

In our scenario this bug caused the mpt driver to pass incorrectly truncated
32-bit addresses in the scatter-gather list, because the data buffer in the high
memory (> 4Gib) was not bounced to < 4Gib memory.

-- Vyacheslav Malyugin, Kaustubh Patil

Fix: 

The use of trunc_page() in the counting code seems questionable.
Comment 1 Mark Tinguely 2010-02-08 14:51:23 UTC
IMO, this problem should be closed for amd64 because it has been fix in
at least FreeBSD 8, if not sooner.

The ARM busdma_machdep.c has this same truncate issue, I have some busdma
extensions that include starting with the newest i386 sources.

--Mark Tinguely.
Comment 2 marktinguely 2010-08-06 13:42:56 UTC
With the bug fixing weekend coming up, revision 176206 fixed this in 
FreeBSD 8 and revision 200289 fixed this in FreeBSD 7. This patch could 
be moved to FreeBSD 6 stable and close this problem report.

--Mark Tinguely.
Comment 3 Jaakko Heinonen freebsd_committer freebsd_triage 2010-09-21 16:17:45 UTC
State Changed
From-To: open->closed

Reportedly fixed in head, stable/8 and stable/7.