From: di...@netcom.com (William R. Dirks) Subject: vmalloc() and DMA Date: 1998/07/18 Message-ID: <dirksEw9pCH.Grn@netcom.com>#1/1 X-Deja-AN: 372536286 Sender: di...@netcom8.netcom.com Organization: Netcom Newsgroups: comp.os.linux.development.system Hello. I am new to Linux, and I have a kernel-mode programming question. I am using RH5.0, with 2.1.106 kernel in place of the 2.0.? kernel. I am writing a modular kernel-mode driver for a PCI video capture card with bus mastering capability. I am having difficulty deciding how to allocate the DMA buffer. The buffer may need to be quite large for a whole image, 200K would not be unusual. The card uses a scatter list so the buffer does not have to be physically contiguous. The only requirement is that all the pages be mapped to physical RAM, and stay that way, and don't physically move. kmalloc() is inappropriate because the buffer is too big, and kmalloc() allocates physically contiguous pages which is unnecessary for me. vmalloc() looks like what I want, but I am unable to use the pages of a vmalloc()ed buffer. Are vmalloc()ed pages fixed in physical RAM, or are they subject to being swapped out to disk? If they are swappable, is there a way to lock them in, like what the PageLock() function does in Windows? What I would *like* to do (roughly): buffer = vmalloc(...); for (i = 0, addr = buffer; ....) { scatterlist[i].addr = virt_to_bus(addr); scatterlist[i].len = PAGE_SIZE; addr += PAGE_SIZE; } The problem is virt_to_bus() does not return a valid physical address. My system has 32M physical RAM, so addresses go 00000000-01FFFFFF. A call to vmalloc() will return something like C2812000, and then virt_to_bus() will return 02812000, which is beyond physical RAM. OTOH, get_free_page() might return something like C0ABC000, and virt_to_bus() gives 00ABC000, which is in physical RAM, and I can DMA to that buffer. I tried using pte_offset() et al. to get the pte's, but they just bring compiler errors. Can I do what I want? If I can't use vmalloc() for this then it looks like I will have to allocate pages one by one with get_free_page(), and copy the data out page by page, etc. But then I am effectively writing my own software MMU and my own software page table, and that is senseless duplication because there is a perfectly good hardware MMU that does exacly what I want, if only I had PageLock() and a function to give me the pte's! I'm ignorant of the in's and out's of Linux. Can anybody advise? Thanks! Bill.
From: a...@muc.de Subject: Re: vmalloc() and DMA Date: 1998/07/18 Message-ID: <m3k95b6bjg.fsf@fred.muc.de>#1/1 X-Deja-AN: 372562481 Distribution: world Sender: an...@fred.muc.de References: <dirksEw9pCH.Grn@netcom.com> Organization: [posted via] Leibniz-Rechenzentrum, Muenchen (Germany) Newsgroups: comp.os.linux.development.system di...@netcom.com (William R. Dirks) writes: > kmalloc() is inappropriate because the buffer is too big, and kmalloc() > allocates physically contiguous pages which is unnecessary for me. > vmalloc() looks like what I want, but I am unable to use the pages of a > vmalloc()ed buffer. Are vmalloc()ed pages fixed in physical RAM, or are > they subject to being swapped out to disk? If they are swappable, is there > a way to lock them in, like what the PageLock() function does in Windows? vmalloc() pages are not swapped, so no special action is required. > Can I do what I want? If I can't use vmalloc() for this then it looks like > I will have to allocate pages one by one with get_free_page(), and copy > the data out page by page, etc. But then I am effectively writing my own > software MMU and my own software page table, and that is senseless > duplication because there is a perfectly good hardware MMU that does > exacly what I want, if only I had PageLock() and a function to give me the > pte's! You unfortunately have to do it by hand, because the hardware won't give you the address. Just vmalloc() the space and then use something like this: /* Only pass page aligned addresses */ unsigned long vm_virt_to_bus(unsigned long addr) { pgd_t *pgd; pmd_t *pmd; pte_t **pte; pgd = pgd_offset(current->mm, addr); if (pgd_none(pgd)) return 0; pmd = pmd_offset(pgd, addr); if (pmd_none(pmd)) return 0; pte = pte_offset(pmd, addr); if (pte_none(pte)) return 0; return virt_to_bus((void *) pte_page(pte)); } -Andi
From: di...@netcom.com (William R. Dirks) Subject: Re: vmalloc() and DMA Date: 1998/07/18 Message-ID: <dirksEwA56I.M43@netcom.com>#1/1 X-Deja-AN: 372591235 Sender: di...@netcom18.netcom.com References: <dirksEw9pCH.Grn@netcom.com> <m3k95b6bjg.fsf@fred.muc.de> Organization: Netcom Newsgroups: comp.os.linux.development.system In article <m3k95b6...@fred.muc.de>, <a...@muc.de> wrote: >di...@netcom.com (William R. Dirks) writes: >>[how to get bus address of vmalloc()ed memory?] > >You unfortunately have to do it by hand, because the hardware won't give >you the address. > >Just vmalloc() the space and then use something like this: >[subroutine to get bus address] Thanks, Andi! I got it working. It's just what I wanted! Andi's code wouldn't compile exactly as he wrote it, so below is a corrected version in case anyone else needs it. I also changed the parameter to void * so it's the same as virt_to_bus(). Bill. /* Only pass page aligned addresses */ unsigned long vm_virt_to_bus(void *virt) { pgd_t *pgd; pmd_t *pmd; pte_t *pte; unsigned long addr = (unsigned long)virt; pgd = pgd_offset(current->mm, addr); if (pgd_none(*pgd)) return 0; pmd = pmd_offset(pgd, addr); if (pmd_none(*pmd)) return 0; pte = pte_offset(pmd, addr); if (pte_none(*pte)) return 0; return virt_to_bus((void *)pte_page(*pte)); }