aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorLinus Torvalds2013-04-16 15:45:37 -0500
committerGreg Kroah-Hartman2013-04-25 14:51:24 -0500
commitd8b1c028720bb2f3ba634c64ac2e834982b7ee7a (patch)
treee0a7c7e0ca8dda9253e2799d2a17de9bf0f04bd6 /mm
parent37ed4a3f3bb107bfb307610b9ab2b87db03df3a6 (diff)
downloadkernel-video-d8b1c028720bb2f3ba634c64ac2e834982b7ee7a.tar.gz
kernel-video-d8b1c028720bb2f3ba634c64ac2e834982b7ee7a.tar.xz
kernel-video-d8b1c028720bb2f3ba634c64ac2e834982b7ee7a.zip
vm: add vm_iomap_memory() helper function
commit b4cbb197c7e7a68dbad0d491242e3ca67420c13e upstream. Various drivers end up replicating the code to mmap() their memory buffers into user space, and our core memory remapping function may be very flexible but it is unnecessarily complicated for the common cases to use. Our internal VM uses pfn's ("page frame numbers") which simplifies things for the VM, and allows us to pass physical addresses around in a denser and more efficient format than passing a "phys_addr_t" around, and having to shift it up and down by the page size. But it just means that drivers end up doing that shifting instead at the interface level. It also means that drivers end up mucking around with internal VM things like the vma details (vm_pgoff, vm_start/end) way more than they really need to. So this just exports a function to map a certain physical memory range into user space (using a phys_addr_t based interface that is much more natural for a driver) and hides all the complexity from the driver. Some drivers will still end up tweaking the vm_page_prot details for things like prefetching or cacheability etc, but that's actually relevant to the driver, rather than caring about what the page offset of the mapping is into the particular IO memory region. Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/memory.c47
1 files changed, 47 insertions, 0 deletions
diff --git a/mm/memory.c b/mm/memory.c
index f8b734a7016..32a495a60cf 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2358,6 +2358,53 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
2358} 2358}
2359EXPORT_SYMBOL(remap_pfn_range); 2359EXPORT_SYMBOL(remap_pfn_range);
2360 2360
2361/**
2362 * vm_iomap_memory - remap memory to userspace
2363 * @vma: user vma to map to
2364 * @start: start of area
2365 * @len: size of area
2366 *
2367 * This is a simplified io_remap_pfn_range() for common driver use. The
2368 * driver just needs to give us the physical memory range to be mapped,
2369 * we'll figure out the rest from the vma information.
2370 *
2371 * NOTE! Some drivers might want to tweak vma->vm_page_prot first to get
2372 * whatever write-combining details or similar.
2373 */
2374int vm_iomap_memory(struct vm_area_struct *vma, phys_addr_t start, unsigned long len)
2375{
2376 unsigned long vm_len, pfn, pages;
2377
2378 /* Check that the physical memory area passed in looks valid */
2379 if (start + len < start)
2380 return -EINVAL;
2381 /*
2382 * You *really* shouldn't map things that aren't page-aligned,
2383 * but we've historically allowed it because IO memory might
2384 * just have smaller alignment.
2385 */
2386 len += start & ~PAGE_MASK;
2387 pfn = start >> PAGE_SHIFT;
2388 pages = (len + ~PAGE_MASK) >> PAGE_SHIFT;
2389 if (pfn + pages < pfn)
2390 return -EINVAL;
2391
2392 /* We start the mapping 'vm_pgoff' pages into the area */
2393 if (vma->vm_pgoff > pages)
2394 return -EINVAL;
2395 pfn += vma->vm_pgoff;
2396 pages -= vma->vm_pgoff;
2397
2398 /* Can we fit all of the mapping? */
2399 vm_len = vma->vm_end - vma->vm_start;
2400 if (vm_len >> PAGE_SHIFT > pages)
2401 return -EINVAL;
2402
2403 /* Ok, let it rip */
2404 return io_remap_pfn_range(vma, vma->vm_start, pfn, vm_len, vma->vm_page_prot);
2405}
2406EXPORT_SYMBOL(vm_iomap_memory);
2407
2361static int apply_to_pte_range(struct mm_struct *mm, pmd_t *pmd, 2408static int apply_to_pte_range(struct mm_struct *mm, pmd_t *pmd,
2362 unsigned long addr, unsigned long end, 2409 unsigned long addr, unsigned long end,
2363 pte_fn_t fn, void *data) 2410 pte_fn_t fn, void *data)