Commit e69e9d4aee712a22665f008ae0550bb3d7c7f7c1
Committed by
Linus Torvalds
1 parent
cef2ac3f6c
Exists in
smarc-imx_3.14.28_1.0.0_ga
and in
1 other branch
vmalloc: introduce remap_vmalloc_range_partial
We want to allocate ELF note segment buffer on the 2nd kernel in vmalloc space and remap it to user-space in order to reduce the risk that memory allocation fails on system with huge number of CPUs and so with huge ELF note segment that exceeds 11-order block size. Although there's already remap_vmalloc_range for the purpose of remapping vmalloc memory to user-space, we need to specify user-space range via vma. Mmap on /proc/vmcore needs to remap range across multiple objects, so the interface that requires vma to cover full range is problematic. This patch introduces remap_vmalloc_range_partial that receives user-space range as a pair of base address and size and can be used for mmap on /proc/vmcore case. remap_vmalloc_range is rewritten using remap_vmalloc_range_partial. [akpm@linux-foundation.org: use PAGE_ALIGNED()] Signed-off-by: HATAYAMA Daisuke <d.hatayama@jp.fujitsu.com> Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Cc: Vivek Goyal <vgoyal@redhat.com> Cc: Atsushi Kumagai <kumagai-atsushi@mxc.nes.nec.co.jp> Cc: Lisa Mitchell <lisa.mitchell@hp.com> Cc: Zhang Yanfei <zhangyanfei@cn.fujitsu.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 2 changed files with 49 additions and 22 deletions Side-by-side Diff
include/linux/vmalloc.h
... | ... | @@ -82,6 +82,10 @@ |
82 | 82 | unsigned long flags, pgprot_t prot); |
83 | 83 | extern void vunmap(const void *addr); |
84 | 84 | |
85 | +extern int remap_vmalloc_range_partial(struct vm_area_struct *vma, | |
86 | + unsigned long uaddr, void *kaddr, | |
87 | + unsigned long size); | |
88 | + | |
85 | 89 | extern int remap_vmalloc_range(struct vm_area_struct *vma, void *addr, |
86 | 90 | unsigned long pgoff); |
87 | 91 | void vmalloc_sync_all(void); |
mm/vmalloc.c
... | ... | @@ -1476,10 +1476,9 @@ |
1476 | 1476 | if (!addr) |
1477 | 1477 | return; |
1478 | 1478 | |
1479 | - if ((PAGE_SIZE-1) & (unsigned long)addr) { | |
1480 | - WARN(1, KERN_ERR "Trying to vfree() bad address (%p)\n", addr); | |
1479 | + if (WARN(!PAGE_ALIGNED(addr), "Trying to vfree() bad address (%p)\n", | |
1480 | + addr)); | |
1481 | 1481 | return; |
1482 | - } | |
1483 | 1482 | |
1484 | 1483 | area = remove_vm_area(addr); |
1485 | 1484 | if (unlikely(!area)) { |
1486 | 1485 | |
1487 | 1486 | |
1488 | 1487 | |
1489 | 1488 | |
1490 | 1489 | |
1491 | 1490 | |
1492 | 1491 | |
1493 | 1492 | |
... | ... | @@ -2148,42 +2147,43 @@ |
2148 | 2147 | } |
2149 | 2148 | |
2150 | 2149 | /** |
2151 | - * remap_vmalloc_range - map vmalloc pages to userspace | |
2152 | - * @vma: vma to cover (map full range of vma) | |
2153 | - * @addr: vmalloc memory | |
2154 | - * @pgoff: number of pages into addr before first page to map | |
2150 | + * remap_vmalloc_range_partial - map vmalloc pages to userspace | |
2151 | + * @vma: vma to cover | |
2152 | + * @uaddr: target user address to start at | |
2153 | + * @kaddr: virtual address of vmalloc kernel memory | |
2154 | + * @size: size of map area | |
2155 | 2155 | * |
2156 | 2156 | * Returns: 0 for success, -Exxx on failure |
2157 | 2157 | * |
2158 | - * This function checks that addr is a valid vmalloc'ed area, and | |
2159 | - * that it is big enough to cover the vma. Will return failure if | |
2160 | - * that criteria isn't met. | |
2158 | + * This function checks that @kaddr is a valid vmalloc'ed area, | |
2159 | + * and that it is big enough to cover the range starting at | |
2160 | + * @uaddr in @vma. Will return failure if that criteria isn't | |
2161 | + * met. | |
2161 | 2162 | * |
2162 | 2163 | * Similar to remap_pfn_range() (see mm/memory.c) |
2163 | 2164 | */ |
2164 | -int remap_vmalloc_range(struct vm_area_struct *vma, void *addr, | |
2165 | - unsigned long pgoff) | |
2165 | +int remap_vmalloc_range_partial(struct vm_area_struct *vma, unsigned long uaddr, | |
2166 | + void *kaddr, unsigned long size) | |
2166 | 2167 | { |
2167 | 2168 | struct vm_struct *area; |
2168 | - unsigned long uaddr = vma->vm_start; | |
2169 | - unsigned long usize = vma->vm_end - vma->vm_start; | |
2170 | 2169 | |
2171 | - if ((PAGE_SIZE-1) & (unsigned long)addr) | |
2170 | + size = PAGE_ALIGN(size); | |
2171 | + | |
2172 | + if (!PAGE_ALIGNED(uaddr) || !PAGE_ALIGNED(kaddr)) | |
2172 | 2173 | return -EINVAL; |
2173 | 2174 | |
2174 | - area = find_vm_area(addr); | |
2175 | + area = find_vm_area(kaddr); | |
2175 | 2176 | if (!area) |
2176 | 2177 | return -EINVAL; |
2177 | 2178 | |
2178 | 2179 | if (!(area->flags & VM_USERMAP)) |
2179 | 2180 | return -EINVAL; |
2180 | 2181 | |
2181 | - if (usize + (pgoff << PAGE_SHIFT) > area->size - PAGE_SIZE) | |
2182 | + if (kaddr + size > area->addr + area->size) | |
2182 | 2183 | return -EINVAL; |
2183 | 2184 | |
2184 | - addr += pgoff << PAGE_SHIFT; | |
2185 | 2185 | do { |
2186 | - struct page *page = vmalloc_to_page(addr); | |
2186 | + struct page *page = vmalloc_to_page(kaddr); | |
2187 | 2187 | int ret; |
2188 | 2188 | |
2189 | 2189 | ret = vm_insert_page(vma, uaddr, page); |
2190 | 2190 | |
... | ... | @@ -2191,13 +2191,36 @@ |
2191 | 2191 | return ret; |
2192 | 2192 | |
2193 | 2193 | uaddr += PAGE_SIZE; |
2194 | - addr += PAGE_SIZE; | |
2195 | - usize -= PAGE_SIZE; | |
2196 | - } while (usize > 0); | |
2194 | + kaddr += PAGE_SIZE; | |
2195 | + size -= PAGE_SIZE; | |
2196 | + } while (size > 0); | |
2197 | 2197 | |
2198 | 2198 | vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; |
2199 | 2199 | |
2200 | 2200 | return 0; |
2201 | +} | |
2202 | +EXPORT_SYMBOL(remap_vmalloc_range_partial); | |
2203 | + | |
2204 | +/** | |
2205 | + * remap_vmalloc_range - map vmalloc pages to userspace | |
2206 | + * @vma: vma to cover (map full range of vma) | |
2207 | + * @addr: vmalloc memory | |
2208 | + * @pgoff: number of pages into addr before first page to map | |
2209 | + * | |
2210 | + * Returns: 0 for success, -Exxx on failure | |
2211 | + * | |
2212 | + * This function checks that addr is a valid vmalloc'ed area, and | |
2213 | + * that it is big enough to cover the vma. Will return failure if | |
2214 | + * that criteria isn't met. | |
2215 | + * | |
2216 | + * Similar to remap_pfn_range() (see mm/memory.c) | |
2217 | + */ | |
2218 | +int remap_vmalloc_range(struct vm_area_struct *vma, void *addr, | |
2219 | + unsigned long pgoff) | |
2220 | +{ | |
2221 | + return remap_vmalloc_range_partial(vma, vma->vm_start, | |
2222 | + addr + (pgoff << PAGE_SHIFT), | |
2223 | + vma->vm_end - vma->vm_start); | |
2201 | 2224 | } |
2202 | 2225 | EXPORT_SYMBOL(remap_vmalloc_range); |
2203 | 2226 |