Skip to content

Commit

Permalink
vmalloc: introduce remap_vmalloc_range_partial
Browse files Browse the repository at this point in the history
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.

[[email protected]: use PAGE_ALIGNED()]
Signed-off-by: HATAYAMA Daisuke <[email protected]>
Cc: KOSAKI Motohiro <[email protected]>
Cc: Vivek Goyal <[email protected]>
Cc: Atsushi Kumagai <[email protected]>
Cc: Lisa Mitchell <[email protected]>
Cc: Zhang Yanfei <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
HATAYAMA Daisuke authored and torvalds committed Jul 3, 2013
1 parent cef2ac3 commit e69e9d4
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 22 deletions.
4 changes: 4 additions & 0 deletions include/linux/vmalloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ extern void *vmap(struct page **pages, unsigned int count,
unsigned long flags, pgprot_t prot);
extern void vunmap(const void *addr);

extern int remap_vmalloc_range_partial(struct vm_area_struct *vma,
unsigned long uaddr, void *kaddr,
unsigned long size);

extern int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,
unsigned long pgoff);
void vmalloc_sync_all(void);
Expand Down
67 changes: 45 additions & 22 deletions mm/vmalloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1476,10 +1476,9 @@ static void __vunmap(const void *addr, int deallocate_pages)
if (!addr)
return;

if ((PAGE_SIZE-1) & (unsigned long)addr) {
WARN(1, KERN_ERR "Trying to vfree() bad address (%p)\n", addr);
if (WARN(!PAGE_ALIGNED(addr), "Trying to vfree() bad address (%p)\n",
addr));
return;
}

area = remove_vm_area(addr);
if (unlikely(!area)) {
Expand Down Expand Up @@ -2148,57 +2147,81 @@ long vwrite(char *buf, char *addr, unsigned long count)
}

/**
* remap_vmalloc_range - map vmalloc pages to userspace
* @vma: vma to cover (map full range of vma)
* @addr: vmalloc memory
* @pgoff: number of pages into addr before first page to map
* remap_vmalloc_range_partial - map vmalloc pages to userspace
* @vma: vma to cover
* @uaddr: target user address to start at
* @kaddr: virtual address of vmalloc kernel memory
* @size: size of map area
*
* Returns: 0 for success, -Exxx on failure
*
* This function checks that addr is a valid vmalloc'ed area, and
* that it is big enough to cover the vma. Will return failure if
* that criteria isn't met.
* This function checks that @kaddr is a valid vmalloc'ed area,
* and that it is big enough to cover the range starting at
* @uaddr in @vma. Will return failure if that criteria isn't
* met.
*
* Similar to remap_pfn_range() (see mm/memory.c)
*/
int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,
unsigned long pgoff)
int remap_vmalloc_range_partial(struct vm_area_struct *vma, unsigned long uaddr,
void *kaddr, unsigned long size)
{
struct vm_struct *area;
unsigned long uaddr = vma->vm_start;
unsigned long usize = vma->vm_end - vma->vm_start;

if ((PAGE_SIZE-1) & (unsigned long)addr)
size = PAGE_ALIGN(size);

if (!PAGE_ALIGNED(uaddr) || !PAGE_ALIGNED(kaddr))
return -EINVAL;

area = find_vm_area(addr);
area = find_vm_area(kaddr);
if (!area)
return -EINVAL;

if (!(area->flags & VM_USERMAP))
return -EINVAL;

if (usize + (pgoff << PAGE_SHIFT) > area->size - PAGE_SIZE)
if (kaddr + size > area->addr + area->size)
return -EINVAL;

addr += pgoff << PAGE_SHIFT;
do {
struct page *page = vmalloc_to_page(addr);
struct page *page = vmalloc_to_page(kaddr);
int ret;

ret = vm_insert_page(vma, uaddr, page);
if (ret)
return ret;

uaddr += PAGE_SIZE;
addr += PAGE_SIZE;
usize -= PAGE_SIZE;
} while (usize > 0);
kaddr += PAGE_SIZE;
size -= PAGE_SIZE;
} while (size > 0);

vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;

return 0;
}
EXPORT_SYMBOL(remap_vmalloc_range_partial);

/**
* remap_vmalloc_range - map vmalloc pages to userspace
* @vma: vma to cover (map full range of vma)
* @addr: vmalloc memory
* @pgoff: number of pages into addr before first page to map
*
* Returns: 0 for success, -Exxx on failure
*
* This function checks that addr is a valid vmalloc'ed area, and
* that it is big enough to cover the vma. Will return failure if
* that criteria isn't met.
*
* Similar to remap_pfn_range() (see mm/memory.c)
*/
int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,
unsigned long pgoff)
{
return remap_vmalloc_range_partial(vma, vma->vm_start,
addr + (pgoff << PAGE_SHIFT),
vma->vm_end - vma->vm_start);
}
EXPORT_SYMBOL(remap_vmalloc_range);

/*
Expand Down

0 comments on commit e69e9d4

Please sign in to comment.