I am mapping multiple physically non-contiguous memory buffers to a single linear user space address. I use vm_insert_page() and get_page(). I need to use get_page() on all allocated pages because only the very first page of a given buffer has a ref count > 0 and vm_insert_page() needs ref count to be > 0. Supposedly (according to some post on the Net) I have to 'free' the pages for which I increment ref count by calling get_page() if no longer needed. However, I'm not quite sure how to 'free' the pages. Do I need to keep track of each page struct returned by get_page() and then call a corresponding API to free the pages during unmapping? It does not look like OS will do it for me automatically. That is, after user process exists, ref counts on the pages stay unchanged and get incremented again on next mmap from user space.
My pseudo code is like this:
allocate multiple phys non-contiguous memory buffers using calls to pci_alloc_consistent()
for all 4K chunks in all buffers allocated above
create a page struct using virt_to_page(phys_chunk_addr)
// this is needed because only the very first page of a phys buffer
// will have ref count > 0 which is needed by vm_insert_page()!
increment page ref count by calling get_page()
place the page in vma using vm_insert_page()
Thanks for any suggestions/pointers.
Dan.
DMA-coherent memory might need architecture-specific caching flags, so you cannot just simply map it to userspace.
pci_alloc_consistent is deprecated.
For mapping a single contiguous memory block, use dma_alloc_coherent and dma_mmap_coherent.
If you aren't too much concerned about portability, you can avoid using dma_alloc_coherent altogether and use individual pages instead:
alloc_page (for PCI, you usually need GFP_DMA32);dma_map_page;vm_insert_page.Please note that this is not a coherent DMA mapping but a streaming DMA mapping.
On x86, this does not matter, but on many other architectures, you would have to call the dma_sync_* functions at the appropriate time.
For an example of this in the kernel, see drivers/firewire/core-iso.c, core-cdev.c.
If you need to have multiple large and physically contiguous buffers mapped into one virtually contiguous area, you cannot use dma_mmap_coherent, so you have to do this the hard way:
dma_alloc_coherent as often as you need it;mmap implementation, just set vm_area_struct->vm_ops;vm_operations_struct.fault, call virt_to_page, call get_page, and set vm_fault->page to the page.For an example, see sound/core/pcm_native.c.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With