f8b7256096
New helper - sys_mmap_pgoff(); switch syscalls to using it. Acked-by: David S. Miller <davem@davemloft.net> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
223 lines
5.6 KiB
C
223 lines
5.6 KiB
C
#include <linux/errno.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/sem.h>
|
|
#include <linux/msg.h>
|
|
#include <linux/shm.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/mman.h>
|
|
#include <linux/file.h>
|
|
#include <linux/utsname.h>
|
|
#include <linux/personality.h>
|
|
#include <linux/random.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#include <asm/ia32.h>
|
|
#include <asm/syscalls.h>
|
|
|
|
SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
|
|
unsigned long, prot, unsigned long, flags,
|
|
unsigned long, fd, unsigned long, off)
|
|
{
|
|
long error;
|
|
error = -EINVAL;
|
|
if (off & ~PAGE_MASK)
|
|
goto out;
|
|
|
|
error = sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
|
|
out:
|
|
return error;
|
|
}
|
|
|
|
static void find_start_end(unsigned long flags, unsigned long *begin,
|
|
unsigned long *end)
|
|
{
|
|
if (!test_thread_flag(TIF_IA32) && (flags & MAP_32BIT)) {
|
|
unsigned long new_begin;
|
|
/* This is usually used needed to map code in small
|
|
model, so it needs to be in the first 31bit. Limit
|
|
it to that. This means we need to move the
|
|
unmapped base down for this case. This can give
|
|
conflicts with the heap, but we assume that glibc
|
|
malloc knows how to fall back to mmap. Give it 1GB
|
|
of playground for now. -AK */
|
|
*begin = 0x40000000;
|
|
*end = 0x80000000;
|
|
if (current->flags & PF_RANDOMIZE) {
|
|
new_begin = randomize_range(*begin, *begin + 0x02000000, 0);
|
|
if (new_begin)
|
|
*begin = new_begin;
|
|
}
|
|
} else {
|
|
*begin = TASK_UNMAPPED_BASE;
|
|
*end = TASK_SIZE;
|
|
}
|
|
}
|
|
|
|
unsigned long
|
|
arch_get_unmapped_area(struct file *filp, unsigned long addr,
|
|
unsigned long len, unsigned long pgoff, unsigned long flags)
|
|
{
|
|
struct mm_struct *mm = current->mm;
|
|
struct vm_area_struct *vma;
|
|
unsigned long start_addr;
|
|
unsigned long begin, end;
|
|
|
|
if (flags & MAP_FIXED)
|
|
return addr;
|
|
|
|
find_start_end(flags, &begin, &end);
|
|
|
|
if (len > end)
|
|
return -ENOMEM;
|
|
|
|
if (addr) {
|
|
addr = PAGE_ALIGN(addr);
|
|
vma = find_vma(mm, addr);
|
|
if (end - len >= addr &&
|
|
(!vma || addr + len <= vma->vm_start))
|
|
return addr;
|
|
}
|
|
if (((flags & MAP_32BIT) || test_thread_flag(TIF_IA32))
|
|
&& len <= mm->cached_hole_size) {
|
|
mm->cached_hole_size = 0;
|
|
mm->free_area_cache = begin;
|
|
}
|
|
addr = mm->free_area_cache;
|
|
if (addr < begin)
|
|
addr = begin;
|
|
start_addr = addr;
|
|
|
|
full_search:
|
|
for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
|
|
/* At this point: (!vma || addr < vma->vm_end). */
|
|
if (end - len < addr) {
|
|
/*
|
|
* Start a new search - just in case we missed
|
|
* some holes.
|
|
*/
|
|
if (start_addr != begin) {
|
|
start_addr = addr = begin;
|
|
mm->cached_hole_size = 0;
|
|
goto full_search;
|
|
}
|
|
return -ENOMEM;
|
|
}
|
|
if (!vma || addr + len <= vma->vm_start) {
|
|
/*
|
|
* Remember the place where we stopped the search:
|
|
*/
|
|
mm->free_area_cache = addr + len;
|
|
return addr;
|
|
}
|
|
if (addr + mm->cached_hole_size < vma->vm_start)
|
|
mm->cached_hole_size = vma->vm_start - addr;
|
|
|
|
addr = vma->vm_end;
|
|
}
|
|
}
|
|
|
|
|
|
unsigned long
|
|
arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
|
|
const unsigned long len, const unsigned long pgoff,
|
|
const unsigned long flags)
|
|
{
|
|
struct vm_area_struct *vma;
|
|
struct mm_struct *mm = current->mm;
|
|
unsigned long addr = addr0;
|
|
|
|
/* requested length too big for entire address space */
|
|
if (len > TASK_SIZE)
|
|
return -ENOMEM;
|
|
|
|
if (flags & MAP_FIXED)
|
|
return addr;
|
|
|
|
/* for MAP_32BIT mappings we force the legact mmap base */
|
|
if (!test_thread_flag(TIF_IA32) && (flags & MAP_32BIT))
|
|
goto bottomup;
|
|
|
|
/* requesting a specific address */
|
|
if (addr) {
|
|
addr = PAGE_ALIGN(addr);
|
|
vma = find_vma(mm, addr);
|
|
if (TASK_SIZE - len >= addr &&
|
|
(!vma || addr + len <= vma->vm_start))
|
|
return addr;
|
|
}
|
|
|
|
/* check if free_area_cache is useful for us */
|
|
if (len <= mm->cached_hole_size) {
|
|
mm->cached_hole_size = 0;
|
|
mm->free_area_cache = mm->mmap_base;
|
|
}
|
|
|
|
/* either no address requested or can't fit in requested address hole */
|
|
addr = mm->free_area_cache;
|
|
|
|
/* make sure it can fit in the remaining address space */
|
|
if (addr > len) {
|
|
vma = find_vma(mm, addr-len);
|
|
if (!vma || addr <= vma->vm_start)
|
|
/* remember the address as a hint for next time */
|
|
return mm->free_area_cache = addr-len;
|
|
}
|
|
|
|
if (mm->mmap_base < len)
|
|
goto bottomup;
|
|
|
|
addr = mm->mmap_base-len;
|
|
|
|
do {
|
|
/*
|
|
* Lookup failure means no vma is above this address,
|
|
* else if new region fits below vma->vm_start,
|
|
* return with success:
|
|
*/
|
|
vma = find_vma(mm, addr);
|
|
if (!vma || addr+len <= vma->vm_start)
|
|
/* remember the address as a hint for next time */
|
|
return mm->free_area_cache = addr;
|
|
|
|
/* remember the largest hole we saw so far */
|
|
if (addr + mm->cached_hole_size < vma->vm_start)
|
|
mm->cached_hole_size = vma->vm_start - addr;
|
|
|
|
/* try just below the current vma->vm_start */
|
|
addr = vma->vm_start-len;
|
|
} while (len < vma->vm_start);
|
|
|
|
bottomup:
|
|
/*
|
|
* A failed mmap() very likely causes application failure,
|
|
* so fall back to the bottom-up function here. This scenario
|
|
* can happen with large stack limits and large mmap()
|
|
* allocations.
|
|
*/
|
|
mm->cached_hole_size = ~0UL;
|
|
mm->free_area_cache = TASK_UNMAPPED_BASE;
|
|
addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
|
|
/*
|
|
* Restore the topdown base:
|
|
*/
|
|
mm->free_area_cache = mm->mmap_base;
|
|
mm->cached_hole_size = ~0UL;
|
|
|
|
return addr;
|
|
}
|
|
|
|
|
|
SYSCALL_DEFINE1(uname, struct new_utsname __user *, name)
|
|
{
|
|
int err;
|
|
down_read(&uts_sem);
|
|
err = copy_to_user(name, utsname(), sizeof(*name));
|
|
up_read(&uts_sem);
|
|
if (personality(current->personality) == PER_LINUX32)
|
|
err |= copy_to_user(&name->machine, "i686", 5);
|
|
return err ? -EFAULT : 0;
|
|
}
|