Date: Fri, 24 Apr 2015 15:13:05 +0200 From: Svatopluk Kraus <onwahe@gmail.com> To: FreeBSD Arch <freebsd-arch@freebsd.org> Subject: bus_dmamap_sync() for bounced client buffers from user address space Message-ID: <CAFHCsPXMjge84AR2cR8KXMXWP4kH2YvuV_uqtPKUvn5C3ygknw@mail.gmail.com>
next in thread | raw e-mail | index | archive | help
DMA can be done on client buffer from user address space. For example, thru bus_dmamap_load_uio() when uio->uio_segflg is UIO_USERSPACE. Such client buffer can bounce and then, it must be copied to and from bounce buffer in bus_dmamap_sync(). Current implementations (in all archs) do not take into account that bus_dmamap_sync() is asynchronous for POSTWRITE and POSTREAD in general. It can be asynchronous for PREWRITE and PREREAD too. For example, in some driver implementations where DMA client buffers operations are buffered. In those cases, simple bcopy() is not correct. Demonstration of current implementation (x86) is the following: ----------------------------- struct bounce_page { vm_offset_t vaddr; /* kva of bounce buffer */ bus_addr_t busaddr; /* Physical address */ vm_offset_t datavaddr; /* kva of client data */ bus_addr_t dataaddr; /* client physical address */ bus_size_t datacount; /* client data count */ STAILQ_ENTRY(bounce_page) links; }; if ((op & BUS_DMASYNC_PREWRITE) != 0) { while (bpage != NULL) { if (bpage->datavaddr != 0) { bcopy((void *)bpage->datavaddr, (void *)bpage->vaddr, bpage->datacount); } else { physcopyout(bpage->dataaddr, (void *)bpage->vaddr, bpage->datacount); } bpage = STAILQ_NEXT(bpage, links); } dmat->bounce_zone->total_bounced++; } ----------------------------- There are two things: (1) datavaddr is not always kva of client data, but sometimes it can be uva of client data. (2) bcopy() can be used only if datavaddr is kva or when map->pmap is current pmap. Note that there is an implication for bus_dmamap_load_uio() with uio->uio_segflg set to UIO_USERSPACE that used physical pages are in-core and wired. See "man bus_dma". There is not public interface to check that map->pmap is current pmap. So one solution is the following: if (bpage->datavaddr >= VM_MIN_KERNEL_ADDRESS) { bcopy(); } else { physcopy(); } If there will be public pmap_is_current() then another solution is the following: if (bpage->datavaddr != 0) && pmap_is_current(map->pmap)) { bcopy(); } else { physcopy(); } The second solution implies that context switch must not happen during bus_dmamap_sync() called from an interrupt routine. However, IMO, it's granted. Note that map->pmap should be always kernel_pmap for datavaddr >= VM_MIN_KERNEL_ADDRESS. Comments, different solutions, or objections? Svatopluk Kraus
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAFHCsPXMjge84AR2cR8KXMXWP4kH2YvuV_uqtPKUvn5C3ygknw>