11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/mm/nommu.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Replacement code for mm functions to support CPU's that don't 51da177e4SLinus Torvalds * have any form of memory management unit (thus no virtual memory). 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * See Documentation/nommu-mmap.txt 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Copyright (c) 2004-2005 David Howells <dhowells@redhat.com> 101da177e4SLinus Torvalds * Copyright (c) 2000-2003 David McCullough <davidm@snapgear.com> 111da177e4SLinus Torvalds * Copyright (c) 2000-2001 D Jeff Dionne <jeff@uClinux.org> 121da177e4SLinus Torvalds * Copyright (c) 2002 Greg Ungerer <gerg@snapgear.com> 131da177e4SLinus Torvalds */ 141da177e4SLinus Torvalds 151da177e4SLinus Torvalds #include <linux/mm.h> 161da177e4SLinus Torvalds #include <linux/mman.h> 171da177e4SLinus Torvalds #include <linux/swap.h> 181da177e4SLinus Torvalds #include <linux/file.h> 191da177e4SLinus Torvalds #include <linux/highmem.h> 201da177e4SLinus Torvalds #include <linux/pagemap.h> 211da177e4SLinus Torvalds #include <linux/slab.h> 221da177e4SLinus Torvalds #include <linux/vmalloc.h> 231da177e4SLinus Torvalds #include <linux/ptrace.h> 241da177e4SLinus Torvalds #include <linux/blkdev.h> 251da177e4SLinus Torvalds #include <linux/backing-dev.h> 261da177e4SLinus Torvalds #include <linux/mount.h> 271da177e4SLinus Torvalds #include <linux/personality.h> 281da177e4SLinus Torvalds #include <linux/security.h> 291da177e4SLinus Torvalds #include <linux/syscalls.h> 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds #include <asm/uaccess.h> 321da177e4SLinus Torvalds #include <asm/tlb.h> 331da177e4SLinus Torvalds #include <asm/tlbflush.h> 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds void *high_memory; 361da177e4SLinus Torvalds struct page *mem_map; 371da177e4SLinus Torvalds unsigned long max_mapnr; 381da177e4SLinus Torvalds unsigned long num_physpages; 391da177e4SLinus Torvalds unsigned long askedalloc, realalloc; 401da177e4SLinus Torvalds atomic_t vm_committed_space = ATOMIC_INIT(0); 411da177e4SLinus Torvalds int sysctl_overcommit_memory = OVERCOMMIT_GUESS; /* heuristic overcommit */ 421da177e4SLinus Torvalds int sysctl_overcommit_ratio = 50; /* default is 50% */ 431da177e4SLinus Torvalds int sysctl_max_map_count = DEFAULT_MAX_MAP_COUNT; 441da177e4SLinus Torvalds int heap_stack_gap = 0; 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds EXPORT_SYMBOL(mem_map); 471da177e4SLinus Torvalds EXPORT_SYMBOL(__vm_enough_memory); 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds /* list of shareable VMAs */ 501da177e4SLinus Torvalds struct rb_root nommu_vma_tree = RB_ROOT; 511da177e4SLinus Torvalds DECLARE_RWSEM(nommu_vma_sem); 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds struct vm_operations_struct generic_file_vm_ops = { 541da177e4SLinus Torvalds }; 551da177e4SLinus Torvalds 5666aa2b4bSGreg Ungerer EXPORT_SYMBOL(vfree); 5766aa2b4bSGreg Ungerer EXPORT_SYMBOL(vmalloc_to_page); 5866aa2b4bSGreg Ungerer EXPORT_SYMBOL(vmalloc_32); 597a9166e3SLuke Yang EXPORT_SYMBOL(vmap); 607a9166e3SLuke Yang EXPORT_SYMBOL(vunmap); 6166aa2b4bSGreg Ungerer 621da177e4SLinus Torvalds /* 631da177e4SLinus Torvalds * Handle all mappings that got truncated by a "truncate()" 641da177e4SLinus Torvalds * system call. 651da177e4SLinus Torvalds * 661da177e4SLinus Torvalds * NOTE! We have to be ready to update the memory sharing 671da177e4SLinus Torvalds * between the file and the memory map for a potential last 681da177e4SLinus Torvalds * incomplete page. Ugly, but necessary. 691da177e4SLinus Torvalds */ 701da177e4SLinus Torvalds int vmtruncate(struct inode *inode, loff_t offset) 711da177e4SLinus Torvalds { 721da177e4SLinus Torvalds struct address_space *mapping = inode->i_mapping; 731da177e4SLinus Torvalds unsigned long limit; 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds if (inode->i_size < offset) 761da177e4SLinus Torvalds goto do_expand; 771da177e4SLinus Torvalds i_size_write(inode, offset); 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds truncate_inode_pages(mapping, offset); 801da177e4SLinus Torvalds goto out_truncate; 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds do_expand: 831da177e4SLinus Torvalds limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur; 841da177e4SLinus Torvalds if (limit != RLIM_INFINITY && offset > limit) 851da177e4SLinus Torvalds goto out_sig; 861da177e4SLinus Torvalds if (offset > inode->i_sb->s_maxbytes) 871da177e4SLinus Torvalds goto out; 881da177e4SLinus Torvalds i_size_write(inode, offset); 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds out_truncate: 911da177e4SLinus Torvalds if (inode->i_op && inode->i_op->truncate) 921da177e4SLinus Torvalds inode->i_op->truncate(inode); 931da177e4SLinus Torvalds return 0; 941da177e4SLinus Torvalds out_sig: 951da177e4SLinus Torvalds send_sig(SIGXFSZ, current, 0); 961da177e4SLinus Torvalds out: 971da177e4SLinus Torvalds return -EFBIG; 981da177e4SLinus Torvalds } 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds EXPORT_SYMBOL(vmtruncate); 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds /* 1031da177e4SLinus Torvalds * Return the total memory allocated for this pointer, not 1041da177e4SLinus Torvalds * just what the caller asked for. 1051da177e4SLinus Torvalds * 1061da177e4SLinus Torvalds * Doesn't have to be accurate, i.e. may have races. 1071da177e4SLinus Torvalds */ 1081da177e4SLinus Torvalds unsigned int kobjsize(const void *objp) 1091da177e4SLinus Torvalds { 1101da177e4SLinus Torvalds struct page *page; 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds if (!objp || !((page = virt_to_page(objp)))) 1131da177e4SLinus Torvalds return 0; 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds if (PageSlab(page)) 1161da177e4SLinus Torvalds return ksize(objp); 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds BUG_ON(page->index < 0); 1191da177e4SLinus Torvalds BUG_ON(page->index >= MAX_ORDER); 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds return (PAGE_SIZE << page->index); 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds /* 1257b4d5b8bSDavid Howells * get a list of pages in an address range belonging to the specified process 1267b4d5b8bSDavid Howells * and indicate the VMA that covers each page 1277b4d5b8bSDavid Howells * - this is potentially dodgy as we may end incrementing the page count of a 1287b4d5b8bSDavid Howells * slab page or a secondary page from a compound page 1297b4d5b8bSDavid Howells * - don't permit access to VMAs that don't support it, such as I/O mappings 1301da177e4SLinus Torvalds */ 1311da177e4SLinus Torvalds int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, 1321da177e4SLinus Torvalds unsigned long start, int len, int write, int force, 1331da177e4SLinus Torvalds struct page **pages, struct vm_area_struct **vmas) 1341da177e4SLinus Torvalds { 135910e46daSSonic Zhang struct vm_area_struct *vma; 1367b4d5b8bSDavid Howells unsigned long vm_flags; 1377b4d5b8bSDavid Howells int i; 1387b4d5b8bSDavid Howells 1397b4d5b8bSDavid Howells /* calculate required read or write permissions. 1407b4d5b8bSDavid Howells * - if 'force' is set, we only require the "MAY" flags. 1417b4d5b8bSDavid Howells */ 1427b4d5b8bSDavid Howells vm_flags = write ? (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD); 1437b4d5b8bSDavid Howells vm_flags &= force ? (VM_MAYREAD | VM_MAYWRITE) : (VM_READ | VM_WRITE); 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds for (i = 0; i < len; i++) { 146910e46daSSonic Zhang vma = find_vma(mm, start); 147910e46daSSonic Zhang if (!vma) 1487b4d5b8bSDavid Howells goto finish_or_fault; 1497b4d5b8bSDavid Howells 1507b4d5b8bSDavid Howells /* protect what we can, including chardevs */ 1517b4d5b8bSDavid Howells if (vma->vm_flags & (VM_IO | VM_PFNMAP) || 1527b4d5b8bSDavid Howells !(vm_flags & vma->vm_flags)) 1537b4d5b8bSDavid Howells goto finish_or_fault; 154910e46daSSonic Zhang 1551da177e4SLinus Torvalds if (pages) { 1561da177e4SLinus Torvalds pages[i] = virt_to_page(start); 1571da177e4SLinus Torvalds if (pages[i]) 1581da177e4SLinus Torvalds page_cache_get(pages[i]); 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds if (vmas) 161910e46daSSonic Zhang vmas[i] = vma; 1621da177e4SLinus Torvalds start += PAGE_SIZE; 1631da177e4SLinus Torvalds } 1647b4d5b8bSDavid Howells 1657b4d5b8bSDavid Howells return i; 1667b4d5b8bSDavid Howells 1677b4d5b8bSDavid Howells finish_or_fault: 1687b4d5b8bSDavid Howells return i ? : -EFAULT; 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds 17166aa2b4bSGreg Ungerer EXPORT_SYMBOL(get_user_pages); 17266aa2b4bSGreg Ungerer 1731da177e4SLinus Torvalds DEFINE_RWLOCK(vmlist_lock); 1741da177e4SLinus Torvalds struct vm_struct *vmlist; 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds void vfree(void *addr) 1771da177e4SLinus Torvalds { 1781da177e4SLinus Torvalds kfree(addr); 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds 181dd0fc66fSAl Viro void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot) 1821da177e4SLinus Torvalds { 1831da177e4SLinus Torvalds /* 1841da177e4SLinus Torvalds * kmalloc doesn't like __GFP_HIGHMEM for some reason 1851da177e4SLinus Torvalds */ 18684097518SNick Piggin return kmalloc(size, (gfp_mask | __GFP_COMP) & ~__GFP_HIGHMEM); 1871da177e4SLinus Torvalds } 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds struct page * vmalloc_to_page(void *addr) 1901da177e4SLinus Torvalds { 1911da177e4SLinus Torvalds return virt_to_page(addr); 1921da177e4SLinus Torvalds } 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds unsigned long vmalloc_to_pfn(void *addr) 1951da177e4SLinus Torvalds { 1961da177e4SLinus Torvalds return page_to_pfn(virt_to_page(addr)); 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds long vread(char *buf, char *addr, unsigned long count) 2011da177e4SLinus Torvalds { 2021da177e4SLinus Torvalds memcpy(buf, addr, count); 2031da177e4SLinus Torvalds return count; 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds long vwrite(char *buf, char *addr, unsigned long count) 2071da177e4SLinus Torvalds { 2081da177e4SLinus Torvalds /* Don't allow overflow */ 2091da177e4SLinus Torvalds if ((unsigned long) addr + count < count) 2101da177e4SLinus Torvalds count = -(unsigned long) addr; 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds memcpy(addr, buf, count); 2131da177e4SLinus Torvalds return(count); 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds /* 2171da177e4SLinus Torvalds * vmalloc - allocate virtually continguos memory 2181da177e4SLinus Torvalds * 2191da177e4SLinus Torvalds * @size: allocation size 2201da177e4SLinus Torvalds * 2211da177e4SLinus Torvalds * Allocate enough pages to cover @size from the page level 2221da177e4SLinus Torvalds * allocator and map them into continguos kernel virtual space. 2231da177e4SLinus Torvalds * 224c1c8897fSMichael Opdenacker * For tight control over page level allocator and protection flags 2251da177e4SLinus Torvalds * use __vmalloc() instead. 2261da177e4SLinus Torvalds */ 2271da177e4SLinus Torvalds void *vmalloc(unsigned long size) 2281da177e4SLinus Torvalds { 2291da177e4SLinus Torvalds return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL); 2301da177e4SLinus Torvalds } 231f6138882SAndrew Morton EXPORT_SYMBOL(vmalloc); 232f6138882SAndrew Morton 233f6138882SAndrew Morton void *vmalloc_node(unsigned long size, int node) 234f6138882SAndrew Morton { 235f6138882SAndrew Morton return vmalloc(size); 236f6138882SAndrew Morton } 237f6138882SAndrew Morton EXPORT_SYMBOL(vmalloc_node); 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds /* 2401da177e4SLinus Torvalds * vmalloc_32 - allocate virtually continguos memory (32bit addressable) 2411da177e4SLinus Torvalds * 2421da177e4SLinus Torvalds * @size: allocation size 2431da177e4SLinus Torvalds * 2441da177e4SLinus Torvalds * Allocate enough 32bit PA addressable pages to cover @size from the 2451da177e4SLinus Torvalds * page level allocator and map them into continguos kernel virtual space. 2461da177e4SLinus Torvalds */ 2471da177e4SLinus Torvalds void *vmalloc_32(unsigned long size) 2481da177e4SLinus Torvalds { 2491da177e4SLinus Torvalds return __vmalloc(size, GFP_KERNEL, PAGE_KERNEL); 2501da177e4SLinus Torvalds } 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds void *vmap(struct page **pages, unsigned int count, unsigned long flags, pgprot_t prot) 2531da177e4SLinus Torvalds { 2541da177e4SLinus Torvalds BUG(); 2551da177e4SLinus Torvalds return NULL; 2561da177e4SLinus Torvalds } 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds void vunmap(void *addr) 2591da177e4SLinus Torvalds { 2601da177e4SLinus Torvalds BUG(); 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds /* 2641da177e4SLinus Torvalds * sys_brk() for the most part doesn't need the global kernel 2651da177e4SLinus Torvalds * lock, except when an application is doing something nasty 2661da177e4SLinus Torvalds * like trying to un-brk an area that has already been mapped 2671da177e4SLinus Torvalds * to a regular file. in this case, the unmapping will need 2681da177e4SLinus Torvalds * to invoke file system routines that need the global lock. 2691da177e4SLinus Torvalds */ 2701da177e4SLinus Torvalds asmlinkage unsigned long sys_brk(unsigned long brk) 2711da177e4SLinus Torvalds { 2721da177e4SLinus Torvalds struct mm_struct *mm = current->mm; 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds if (brk < mm->start_brk || brk > mm->context.end_brk) 2751da177e4SLinus Torvalds return mm->brk; 2761da177e4SLinus Torvalds 2771da177e4SLinus Torvalds if (mm->brk == brk) 2781da177e4SLinus Torvalds return mm->brk; 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds /* 2811da177e4SLinus Torvalds * Always allow shrinking brk 2821da177e4SLinus Torvalds */ 2831da177e4SLinus Torvalds if (brk <= mm->brk) { 2841da177e4SLinus Torvalds mm->brk = brk; 2851da177e4SLinus Torvalds return brk; 2861da177e4SLinus Torvalds } 2871da177e4SLinus Torvalds 2881da177e4SLinus Torvalds /* 2891da177e4SLinus Torvalds * Ok, looks good - let it rip. 2901da177e4SLinus Torvalds */ 2911da177e4SLinus Torvalds return mm->brk = brk; 2921da177e4SLinus Torvalds } 2931da177e4SLinus Torvalds 2941da177e4SLinus Torvalds #ifdef DEBUG 2951da177e4SLinus Torvalds static void show_process_blocks(void) 2961da177e4SLinus Torvalds { 2971da177e4SLinus Torvalds struct vm_list_struct *vml; 2981da177e4SLinus Torvalds 2991da177e4SLinus Torvalds printk("Process blocks %d:", current->pid); 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds for (vml = ¤t->mm->context.vmlist; vml; vml = vml->next) { 3021da177e4SLinus Torvalds printk(" %p: %p", vml, vml->vma); 3031da177e4SLinus Torvalds if (vml->vma) 3041da177e4SLinus Torvalds printk(" (%d @%lx #%d)", 3051da177e4SLinus Torvalds kobjsize((void *) vml->vma->vm_start), 3061da177e4SLinus Torvalds vml->vma->vm_start, 3071da177e4SLinus Torvalds atomic_read(&vml->vma->vm_usage)); 3081da177e4SLinus Torvalds printk(vml->next ? " ->" : ".\n"); 3091da177e4SLinus Torvalds } 3101da177e4SLinus Torvalds } 3111da177e4SLinus Torvalds #endif /* DEBUG */ 3121da177e4SLinus Torvalds 3133034097aSDavid Howells /* 3143034097aSDavid Howells * add a VMA into a process's mm_struct in the appropriate place in the list 3153034097aSDavid Howells * - should be called with mm->mmap_sem held writelocked 3163034097aSDavid Howells */ 3173034097aSDavid Howells static void add_vma_to_mm(struct mm_struct *mm, struct vm_list_struct *vml) 3183034097aSDavid Howells { 3193034097aSDavid Howells struct vm_list_struct **ppv; 3203034097aSDavid Howells 3213034097aSDavid Howells for (ppv = ¤t->mm->context.vmlist; *ppv; ppv = &(*ppv)->next) 3223034097aSDavid Howells if ((*ppv)->vma->vm_start > vml->vma->vm_start) 3233034097aSDavid Howells break; 3243034097aSDavid Howells 3253034097aSDavid Howells vml->next = *ppv; 3263034097aSDavid Howells *ppv = vml; 3273034097aSDavid Howells } 3283034097aSDavid Howells 3293034097aSDavid Howells /* 3303034097aSDavid Howells * look up the first VMA in which addr resides, NULL if none 3313034097aSDavid Howells * - should be called with mm->mmap_sem at least held readlocked 3323034097aSDavid Howells */ 3333034097aSDavid Howells struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr) 3343034097aSDavid Howells { 3353034097aSDavid Howells struct vm_list_struct *loop, *vml; 3363034097aSDavid Howells 3373034097aSDavid Howells /* search the vm_start ordered list */ 3383034097aSDavid Howells vml = NULL; 3393034097aSDavid Howells for (loop = mm->context.vmlist; loop; loop = loop->next) { 3403034097aSDavid Howells if (loop->vma->vm_start > addr) 3413034097aSDavid Howells break; 3423034097aSDavid Howells vml = loop; 3433034097aSDavid Howells } 3443034097aSDavid Howells 3453034097aSDavid Howells if (vml && vml->vma->vm_end > addr) 3463034097aSDavid Howells return vml->vma; 3473034097aSDavid Howells 3483034097aSDavid Howells return NULL; 3493034097aSDavid Howells } 3503034097aSDavid Howells EXPORT_SYMBOL(find_vma); 3513034097aSDavid Howells 3523034097aSDavid Howells /* 353930e652aSDavid Howells * find a VMA 354930e652aSDavid Howells * - we don't extend stack VMAs under NOMMU conditions 355930e652aSDavid Howells */ 356930e652aSDavid Howells struct vm_area_struct *find_extend_vma(struct mm_struct *mm, unsigned long addr) 357930e652aSDavid Howells { 358930e652aSDavid Howells return find_vma(mm, addr); 359930e652aSDavid Howells } 360930e652aSDavid Howells 361930e652aSDavid Howells /* 3626fa5f80bSDavid Howells * look up the first VMA exactly that exactly matches addr 3636fa5f80bSDavid Howells * - should be called with mm->mmap_sem at least held readlocked 3646fa5f80bSDavid Howells */ 3656fa5f80bSDavid Howells static inline struct vm_area_struct *find_vma_exact(struct mm_struct *mm, 3666fa5f80bSDavid Howells unsigned long addr) 3676fa5f80bSDavid Howells { 3686fa5f80bSDavid Howells struct vm_list_struct *vml; 3696fa5f80bSDavid Howells 3706fa5f80bSDavid Howells /* search the vm_start ordered list */ 3716fa5f80bSDavid Howells for (vml = mm->context.vmlist; vml; vml = vml->next) { 3726fa5f80bSDavid Howells if (vml->vma->vm_start == addr) 3736fa5f80bSDavid Howells return vml->vma; 3746fa5f80bSDavid Howells if (vml->vma->vm_start > addr) 3756fa5f80bSDavid Howells break; 3766fa5f80bSDavid Howells } 3776fa5f80bSDavid Howells 3786fa5f80bSDavid Howells return NULL; 3796fa5f80bSDavid Howells } 3806fa5f80bSDavid Howells 3816fa5f80bSDavid Howells /* 3823034097aSDavid Howells * find a VMA in the global tree 3833034097aSDavid Howells */ 3841da177e4SLinus Torvalds static inline struct vm_area_struct *find_nommu_vma(unsigned long start) 3851da177e4SLinus Torvalds { 3861da177e4SLinus Torvalds struct vm_area_struct *vma; 3871da177e4SLinus Torvalds struct rb_node *n = nommu_vma_tree.rb_node; 3881da177e4SLinus Torvalds 3891da177e4SLinus Torvalds while (n) { 3901da177e4SLinus Torvalds vma = rb_entry(n, struct vm_area_struct, vm_rb); 3911da177e4SLinus Torvalds 3921da177e4SLinus Torvalds if (start < vma->vm_start) 3931da177e4SLinus Torvalds n = n->rb_left; 3941da177e4SLinus Torvalds else if (start > vma->vm_start) 3951da177e4SLinus Torvalds n = n->rb_right; 3961da177e4SLinus Torvalds else 3971da177e4SLinus Torvalds return vma; 3981da177e4SLinus Torvalds } 3991da177e4SLinus Torvalds 4001da177e4SLinus Torvalds return NULL; 4011da177e4SLinus Torvalds } 4021da177e4SLinus Torvalds 4033034097aSDavid Howells /* 4043034097aSDavid Howells * add a VMA in the global tree 4053034097aSDavid Howells */ 4061da177e4SLinus Torvalds static void add_nommu_vma(struct vm_area_struct *vma) 4071da177e4SLinus Torvalds { 4081da177e4SLinus Torvalds struct vm_area_struct *pvma; 4091da177e4SLinus Torvalds struct address_space *mapping; 4101da177e4SLinus Torvalds struct rb_node **p = &nommu_vma_tree.rb_node; 4111da177e4SLinus Torvalds struct rb_node *parent = NULL; 4121da177e4SLinus Torvalds 4131da177e4SLinus Torvalds /* add the VMA to the mapping */ 4141da177e4SLinus Torvalds if (vma->vm_file) { 4151da177e4SLinus Torvalds mapping = vma->vm_file->f_mapping; 4161da177e4SLinus Torvalds 4171da177e4SLinus Torvalds flush_dcache_mmap_lock(mapping); 4181da177e4SLinus Torvalds vma_prio_tree_insert(vma, &mapping->i_mmap); 4191da177e4SLinus Torvalds flush_dcache_mmap_unlock(mapping); 4201da177e4SLinus Torvalds } 4211da177e4SLinus Torvalds 4221da177e4SLinus Torvalds /* add the VMA to the master list */ 4231da177e4SLinus Torvalds while (*p) { 4241da177e4SLinus Torvalds parent = *p; 4251da177e4SLinus Torvalds pvma = rb_entry(parent, struct vm_area_struct, vm_rb); 4261da177e4SLinus Torvalds 4271da177e4SLinus Torvalds if (vma->vm_start < pvma->vm_start) { 4281da177e4SLinus Torvalds p = &(*p)->rb_left; 4291da177e4SLinus Torvalds } 4301da177e4SLinus Torvalds else if (vma->vm_start > pvma->vm_start) { 4311da177e4SLinus Torvalds p = &(*p)->rb_right; 4321da177e4SLinus Torvalds } 4331da177e4SLinus Torvalds else { 4341da177e4SLinus Torvalds /* mappings are at the same address - this can only 4351da177e4SLinus Torvalds * happen for shared-mem chardevs and shared file 4361da177e4SLinus Torvalds * mappings backed by ramfs/tmpfs */ 4371da177e4SLinus Torvalds BUG_ON(!(pvma->vm_flags & VM_SHARED)); 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds if (vma < pvma) 4401da177e4SLinus Torvalds p = &(*p)->rb_left; 4411da177e4SLinus Torvalds else if (vma > pvma) 4421da177e4SLinus Torvalds p = &(*p)->rb_right; 4431da177e4SLinus Torvalds else 4441da177e4SLinus Torvalds BUG(); 4451da177e4SLinus Torvalds } 4461da177e4SLinus Torvalds } 4471da177e4SLinus Torvalds 4481da177e4SLinus Torvalds rb_link_node(&vma->vm_rb, parent, p); 4491da177e4SLinus Torvalds rb_insert_color(&vma->vm_rb, &nommu_vma_tree); 4501da177e4SLinus Torvalds } 4511da177e4SLinus Torvalds 4523034097aSDavid Howells /* 4533034097aSDavid Howells * delete a VMA from the global list 4543034097aSDavid Howells */ 4551da177e4SLinus Torvalds static void delete_nommu_vma(struct vm_area_struct *vma) 4561da177e4SLinus Torvalds { 4571da177e4SLinus Torvalds struct address_space *mapping; 4581da177e4SLinus Torvalds 4591da177e4SLinus Torvalds /* remove the VMA from the mapping */ 4601da177e4SLinus Torvalds if (vma->vm_file) { 4611da177e4SLinus Torvalds mapping = vma->vm_file->f_mapping; 4621da177e4SLinus Torvalds 4631da177e4SLinus Torvalds flush_dcache_mmap_lock(mapping); 4641da177e4SLinus Torvalds vma_prio_tree_remove(vma, &mapping->i_mmap); 4651da177e4SLinus Torvalds flush_dcache_mmap_unlock(mapping); 4661da177e4SLinus Torvalds } 4671da177e4SLinus Torvalds 4681da177e4SLinus Torvalds /* remove from the master list */ 4691da177e4SLinus Torvalds rb_erase(&vma->vm_rb, &nommu_vma_tree); 4701da177e4SLinus Torvalds } 4711da177e4SLinus Torvalds 4721da177e4SLinus Torvalds /* 4731da177e4SLinus Torvalds * determine whether a mapping should be permitted and, if so, what sort of 4741da177e4SLinus Torvalds * mapping we're capable of supporting 4751da177e4SLinus Torvalds */ 4761da177e4SLinus Torvalds static int validate_mmap_request(struct file *file, 4771da177e4SLinus Torvalds unsigned long addr, 4781da177e4SLinus Torvalds unsigned long len, 4791da177e4SLinus Torvalds unsigned long prot, 4801da177e4SLinus Torvalds unsigned long flags, 4811da177e4SLinus Torvalds unsigned long pgoff, 4821da177e4SLinus Torvalds unsigned long *_capabilities) 4831da177e4SLinus Torvalds { 4841da177e4SLinus Torvalds unsigned long capabilities; 4851da177e4SLinus Torvalds unsigned long reqprot = prot; 4861da177e4SLinus Torvalds int ret; 4871da177e4SLinus Torvalds 4881da177e4SLinus Torvalds /* do the simple checks first */ 4891da177e4SLinus Torvalds if (flags & MAP_FIXED || addr) { 4901da177e4SLinus Torvalds printk(KERN_DEBUG 4911da177e4SLinus Torvalds "%d: Can't do fixed-address/overlay mmap of RAM\n", 4921da177e4SLinus Torvalds current->pid); 4931da177e4SLinus Torvalds return -EINVAL; 4941da177e4SLinus Torvalds } 4951da177e4SLinus Torvalds 4961da177e4SLinus Torvalds if ((flags & MAP_TYPE) != MAP_PRIVATE && 4971da177e4SLinus Torvalds (flags & MAP_TYPE) != MAP_SHARED) 4981da177e4SLinus Torvalds return -EINVAL; 4991da177e4SLinus Torvalds 500f81cff0dSMike Frysinger if (!len) 5011da177e4SLinus Torvalds return -EINVAL; 5021da177e4SLinus Torvalds 503f81cff0dSMike Frysinger /* Careful about overflows.. */ 504f81cff0dSMike Frysinger len = PAGE_ALIGN(len); 505f81cff0dSMike Frysinger if (!len || len > TASK_SIZE) 506f81cff0dSMike Frysinger return -ENOMEM; 507f81cff0dSMike Frysinger 5081da177e4SLinus Torvalds /* offset overflow? */ 5091da177e4SLinus Torvalds if ((pgoff + (len >> PAGE_SHIFT)) < pgoff) 510f81cff0dSMike Frysinger return -EOVERFLOW; 5111da177e4SLinus Torvalds 5121da177e4SLinus Torvalds if (file) { 5131da177e4SLinus Torvalds /* validate file mapping requests */ 5141da177e4SLinus Torvalds struct address_space *mapping; 5151da177e4SLinus Torvalds 5161da177e4SLinus Torvalds /* files must support mmap */ 5171da177e4SLinus Torvalds if (!file->f_op || !file->f_op->mmap) 5181da177e4SLinus Torvalds return -ENODEV; 5191da177e4SLinus Torvalds 5201da177e4SLinus Torvalds /* work out if what we've got could possibly be shared 5211da177e4SLinus Torvalds * - we support chardevs that provide their own "memory" 5221da177e4SLinus Torvalds * - we support files/blockdevs that are memory backed 5231da177e4SLinus Torvalds */ 5241da177e4SLinus Torvalds mapping = file->f_mapping; 5251da177e4SLinus Torvalds if (!mapping) 526e9536ae7SJosef Sipek mapping = file->f_path.dentry->d_inode->i_mapping; 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds capabilities = 0; 5291da177e4SLinus Torvalds if (mapping && mapping->backing_dev_info) 5301da177e4SLinus Torvalds capabilities = mapping->backing_dev_info->capabilities; 5311da177e4SLinus Torvalds 5321da177e4SLinus Torvalds if (!capabilities) { 5331da177e4SLinus Torvalds /* no explicit capabilities set, so assume some 5341da177e4SLinus Torvalds * defaults */ 535e9536ae7SJosef Sipek switch (file->f_path.dentry->d_inode->i_mode & S_IFMT) { 5361da177e4SLinus Torvalds case S_IFREG: 5371da177e4SLinus Torvalds case S_IFBLK: 5381da177e4SLinus Torvalds capabilities = BDI_CAP_MAP_COPY; 5391da177e4SLinus Torvalds break; 5401da177e4SLinus Torvalds 5411da177e4SLinus Torvalds case S_IFCHR: 5421da177e4SLinus Torvalds capabilities = 5431da177e4SLinus Torvalds BDI_CAP_MAP_DIRECT | 5441da177e4SLinus Torvalds BDI_CAP_READ_MAP | 5451da177e4SLinus Torvalds BDI_CAP_WRITE_MAP; 5461da177e4SLinus Torvalds break; 5471da177e4SLinus Torvalds 5481da177e4SLinus Torvalds default: 5491da177e4SLinus Torvalds return -EINVAL; 5501da177e4SLinus Torvalds } 5511da177e4SLinus Torvalds } 5521da177e4SLinus Torvalds 5531da177e4SLinus Torvalds /* eliminate any capabilities that we can't support on this 5541da177e4SLinus Torvalds * device */ 5551da177e4SLinus Torvalds if (!file->f_op->get_unmapped_area) 5561da177e4SLinus Torvalds capabilities &= ~BDI_CAP_MAP_DIRECT; 5571da177e4SLinus Torvalds if (!file->f_op->read) 5581da177e4SLinus Torvalds capabilities &= ~BDI_CAP_MAP_COPY; 5591da177e4SLinus Torvalds 5601da177e4SLinus Torvalds if (flags & MAP_SHARED) { 5611da177e4SLinus Torvalds /* do checks for writing, appending and locking */ 5621da177e4SLinus Torvalds if ((prot & PROT_WRITE) && 5631da177e4SLinus Torvalds !(file->f_mode & FMODE_WRITE)) 5641da177e4SLinus Torvalds return -EACCES; 5651da177e4SLinus Torvalds 566e9536ae7SJosef Sipek if (IS_APPEND(file->f_path.dentry->d_inode) && 5671da177e4SLinus Torvalds (file->f_mode & FMODE_WRITE)) 5681da177e4SLinus Torvalds return -EACCES; 5691da177e4SLinus Torvalds 570e9536ae7SJosef Sipek if (locks_verify_locked(file->f_path.dentry->d_inode)) 5711da177e4SLinus Torvalds return -EAGAIN; 5721da177e4SLinus Torvalds 5731da177e4SLinus Torvalds if (!(capabilities & BDI_CAP_MAP_DIRECT)) 5741da177e4SLinus Torvalds return -ENODEV; 5751da177e4SLinus Torvalds 5761da177e4SLinus Torvalds if (((prot & PROT_READ) && !(capabilities & BDI_CAP_READ_MAP)) || 5771da177e4SLinus Torvalds ((prot & PROT_WRITE) && !(capabilities & BDI_CAP_WRITE_MAP)) || 5781da177e4SLinus Torvalds ((prot & PROT_EXEC) && !(capabilities & BDI_CAP_EXEC_MAP)) 5791da177e4SLinus Torvalds ) { 5801da177e4SLinus Torvalds printk("MAP_SHARED not completely supported on !MMU\n"); 5811da177e4SLinus Torvalds return -EINVAL; 5821da177e4SLinus Torvalds } 5831da177e4SLinus Torvalds 5841da177e4SLinus Torvalds /* we mustn't privatise shared mappings */ 5851da177e4SLinus Torvalds capabilities &= ~BDI_CAP_MAP_COPY; 5861da177e4SLinus Torvalds } 5871da177e4SLinus Torvalds else { 5881da177e4SLinus Torvalds /* we're going to read the file into private memory we 5891da177e4SLinus Torvalds * allocate */ 5901da177e4SLinus Torvalds if (!(capabilities & BDI_CAP_MAP_COPY)) 5911da177e4SLinus Torvalds return -ENODEV; 5921da177e4SLinus Torvalds 5931da177e4SLinus Torvalds /* we don't permit a private writable mapping to be 5941da177e4SLinus Torvalds * shared with the backing device */ 5951da177e4SLinus Torvalds if (prot & PROT_WRITE) 5961da177e4SLinus Torvalds capabilities &= ~BDI_CAP_MAP_DIRECT; 5971da177e4SLinus Torvalds } 5981da177e4SLinus Torvalds 5991da177e4SLinus Torvalds /* handle executable mappings and implied executable 6001da177e4SLinus Torvalds * mappings */ 601e9536ae7SJosef Sipek if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) { 6021da177e4SLinus Torvalds if (prot & PROT_EXEC) 6031da177e4SLinus Torvalds return -EPERM; 6041da177e4SLinus Torvalds } 6051da177e4SLinus Torvalds else if ((prot & PROT_READ) && !(prot & PROT_EXEC)) { 6061da177e4SLinus Torvalds /* handle implication of PROT_EXEC by PROT_READ */ 6071da177e4SLinus Torvalds if (current->personality & READ_IMPLIES_EXEC) { 6081da177e4SLinus Torvalds if (capabilities & BDI_CAP_EXEC_MAP) 6091da177e4SLinus Torvalds prot |= PROT_EXEC; 6101da177e4SLinus Torvalds } 6111da177e4SLinus Torvalds } 6121da177e4SLinus Torvalds else if ((prot & PROT_READ) && 6131da177e4SLinus Torvalds (prot & PROT_EXEC) && 6141da177e4SLinus Torvalds !(capabilities & BDI_CAP_EXEC_MAP) 6151da177e4SLinus Torvalds ) { 6161da177e4SLinus Torvalds /* backing file is not executable, try to copy */ 6171da177e4SLinus Torvalds capabilities &= ~BDI_CAP_MAP_DIRECT; 6181da177e4SLinus Torvalds } 6191da177e4SLinus Torvalds } 6201da177e4SLinus Torvalds else { 6211da177e4SLinus Torvalds /* anonymous mappings are always memory backed and can be 6221da177e4SLinus Torvalds * privately mapped 6231da177e4SLinus Torvalds */ 6241da177e4SLinus Torvalds capabilities = BDI_CAP_MAP_COPY; 6251da177e4SLinus Torvalds 6261da177e4SLinus Torvalds /* handle PROT_EXEC implication by PROT_READ */ 6271da177e4SLinus Torvalds if ((prot & PROT_READ) && 6281da177e4SLinus Torvalds (current->personality & READ_IMPLIES_EXEC)) 6291da177e4SLinus Torvalds prot |= PROT_EXEC; 6301da177e4SLinus Torvalds } 6311da177e4SLinus Torvalds 6321da177e4SLinus Torvalds /* allow the security API to have its say */ 6331da177e4SLinus Torvalds ret = security_file_mmap(file, reqprot, prot, flags); 6341da177e4SLinus Torvalds if (ret < 0) 6351da177e4SLinus Torvalds return ret; 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds /* looks okay */ 6381da177e4SLinus Torvalds *_capabilities = capabilities; 6391da177e4SLinus Torvalds return 0; 6401da177e4SLinus Torvalds } 6411da177e4SLinus Torvalds 6421da177e4SLinus Torvalds /* 6431da177e4SLinus Torvalds * we've determined that we can make the mapping, now translate what we 6441da177e4SLinus Torvalds * now know into VMA flags 6451da177e4SLinus Torvalds */ 6461da177e4SLinus Torvalds static unsigned long determine_vm_flags(struct file *file, 6471da177e4SLinus Torvalds unsigned long prot, 6481da177e4SLinus Torvalds unsigned long flags, 6491da177e4SLinus Torvalds unsigned long capabilities) 6501da177e4SLinus Torvalds { 6511da177e4SLinus Torvalds unsigned long vm_flags; 6521da177e4SLinus Torvalds 6531da177e4SLinus Torvalds vm_flags = calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags); 6541da177e4SLinus Torvalds vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; 6551da177e4SLinus Torvalds /* vm_flags |= mm->def_flags; */ 6561da177e4SLinus Torvalds 6571da177e4SLinus Torvalds if (!(capabilities & BDI_CAP_MAP_DIRECT)) { 6581da177e4SLinus Torvalds /* attempt to share read-only copies of mapped file chunks */ 6591da177e4SLinus Torvalds if (file && !(prot & PROT_WRITE)) 6601da177e4SLinus Torvalds vm_flags |= VM_MAYSHARE; 6611da177e4SLinus Torvalds } 6621da177e4SLinus Torvalds else { 6631da177e4SLinus Torvalds /* overlay a shareable mapping on the backing device or inode 6641da177e4SLinus Torvalds * if possible - used for chardevs, ramfs/tmpfs/shmfs and 6651da177e4SLinus Torvalds * romfs/cramfs */ 6661da177e4SLinus Torvalds if (flags & MAP_SHARED) 6671da177e4SLinus Torvalds vm_flags |= VM_MAYSHARE | VM_SHARED; 6681da177e4SLinus Torvalds else if ((((vm_flags & capabilities) ^ vm_flags) & BDI_CAP_VMFLAGS) == 0) 6691da177e4SLinus Torvalds vm_flags |= VM_MAYSHARE; 6701da177e4SLinus Torvalds } 6711da177e4SLinus Torvalds 6721da177e4SLinus Torvalds /* refuse to let anyone share private mappings with this process if 6731da177e4SLinus Torvalds * it's being traced - otherwise breakpoints set in it may interfere 6741da177e4SLinus Torvalds * with another untraced process 6751da177e4SLinus Torvalds */ 6761da177e4SLinus Torvalds if ((flags & MAP_PRIVATE) && (current->ptrace & PT_PTRACED)) 6771da177e4SLinus Torvalds vm_flags &= ~VM_MAYSHARE; 6781da177e4SLinus Torvalds 6791da177e4SLinus Torvalds return vm_flags; 6801da177e4SLinus Torvalds } 6811da177e4SLinus Torvalds 6821da177e4SLinus Torvalds /* 6831da177e4SLinus Torvalds * set up a shared mapping on a file 6841da177e4SLinus Torvalds */ 6851da177e4SLinus Torvalds static int do_mmap_shared_file(struct vm_area_struct *vma, unsigned long len) 6861da177e4SLinus Torvalds { 6871da177e4SLinus Torvalds int ret; 6881da177e4SLinus Torvalds 6891da177e4SLinus Torvalds ret = vma->vm_file->f_op->mmap(vma->vm_file, vma); 6901da177e4SLinus Torvalds if (ret != -ENOSYS) 6911da177e4SLinus Torvalds return ret; 6921da177e4SLinus Torvalds 6931da177e4SLinus Torvalds /* getting an ENOSYS error indicates that direct mmap isn't 6941da177e4SLinus Torvalds * possible (as opposed to tried but failed) so we'll fall 6951da177e4SLinus Torvalds * through to making a private copy of the data and mapping 6961da177e4SLinus Torvalds * that if we can */ 6971da177e4SLinus Torvalds return -ENODEV; 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds 7001da177e4SLinus Torvalds /* 7011da177e4SLinus Torvalds * set up a private mapping or an anonymous shared mapping 7021da177e4SLinus Torvalds */ 7031da177e4SLinus Torvalds static int do_mmap_private(struct vm_area_struct *vma, unsigned long len) 7041da177e4SLinus Torvalds { 7051da177e4SLinus Torvalds void *base; 7061da177e4SLinus Torvalds int ret; 7071da177e4SLinus Torvalds 7081da177e4SLinus Torvalds /* invoke the file's mapping function so that it can keep track of 7091da177e4SLinus Torvalds * shared mappings on devices or memory 7101da177e4SLinus Torvalds * - VM_MAYSHARE will be set if it may attempt to share 7111da177e4SLinus Torvalds */ 7121da177e4SLinus Torvalds if (vma->vm_file) { 7131da177e4SLinus Torvalds ret = vma->vm_file->f_op->mmap(vma->vm_file, vma); 7141da177e4SLinus Torvalds if (ret != -ENOSYS) { 7151da177e4SLinus Torvalds /* shouldn't return success if we're not sharing */ 7161da177e4SLinus Torvalds BUG_ON(ret == 0 && !(vma->vm_flags & VM_MAYSHARE)); 7171da177e4SLinus Torvalds return ret; /* success or a real error */ 7181da177e4SLinus Torvalds } 7191da177e4SLinus Torvalds 7201da177e4SLinus Torvalds /* getting an ENOSYS error indicates that direct mmap isn't 7211da177e4SLinus Torvalds * possible (as opposed to tried but failed) so we'll try to 7221da177e4SLinus Torvalds * make a private copy of the data and map that instead */ 7231da177e4SLinus Torvalds } 7241da177e4SLinus Torvalds 7251da177e4SLinus Torvalds /* allocate some memory to hold the mapping 7261da177e4SLinus Torvalds * - note that this may not return a page-aligned address if the object 7271da177e4SLinus Torvalds * we're allocating is smaller than a page 7281da177e4SLinus Torvalds */ 72984097518SNick Piggin base = kmalloc(len, GFP_KERNEL|__GFP_COMP); 7301da177e4SLinus Torvalds if (!base) 7311da177e4SLinus Torvalds goto enomem; 7321da177e4SLinus Torvalds 7331da177e4SLinus Torvalds vma->vm_start = (unsigned long) base; 7341da177e4SLinus Torvalds vma->vm_end = vma->vm_start + len; 7351da177e4SLinus Torvalds vma->vm_flags |= VM_MAPPED_COPY; 7361da177e4SLinus Torvalds 7371da177e4SLinus Torvalds #ifdef WARN_ON_SLACK 7381da177e4SLinus Torvalds if (len + WARN_ON_SLACK <= kobjsize(result)) 7391da177e4SLinus Torvalds printk("Allocation of %lu bytes from process %d has %lu bytes of slack\n", 7401da177e4SLinus Torvalds len, current->pid, kobjsize(result) - len); 7411da177e4SLinus Torvalds #endif 7421da177e4SLinus Torvalds 7431da177e4SLinus Torvalds if (vma->vm_file) { 7441da177e4SLinus Torvalds /* read the contents of a file into the copy */ 7451da177e4SLinus Torvalds mm_segment_t old_fs; 7461da177e4SLinus Torvalds loff_t fpos; 7471da177e4SLinus Torvalds 7481da177e4SLinus Torvalds fpos = vma->vm_pgoff; 7491da177e4SLinus Torvalds fpos <<= PAGE_SHIFT; 7501da177e4SLinus Torvalds 7511da177e4SLinus Torvalds old_fs = get_fs(); 7521da177e4SLinus Torvalds set_fs(KERNEL_DS); 7531da177e4SLinus Torvalds ret = vma->vm_file->f_op->read(vma->vm_file, base, len, &fpos); 7541da177e4SLinus Torvalds set_fs(old_fs); 7551da177e4SLinus Torvalds 7561da177e4SLinus Torvalds if (ret < 0) 7571da177e4SLinus Torvalds goto error_free; 7581da177e4SLinus Torvalds 7591da177e4SLinus Torvalds /* clear the last little bit */ 7601da177e4SLinus Torvalds if (ret < len) 7611da177e4SLinus Torvalds memset(base + ret, 0, len - ret); 7621da177e4SLinus Torvalds 7631da177e4SLinus Torvalds } else { 7641da177e4SLinus Torvalds /* if it's an anonymous mapping, then just clear it */ 7651da177e4SLinus Torvalds memset(base, 0, len); 7661da177e4SLinus Torvalds } 7671da177e4SLinus Torvalds 7681da177e4SLinus Torvalds return 0; 7691da177e4SLinus Torvalds 7701da177e4SLinus Torvalds error_free: 7711da177e4SLinus Torvalds kfree(base); 7721da177e4SLinus Torvalds vma->vm_start = 0; 7731da177e4SLinus Torvalds return ret; 7741da177e4SLinus Torvalds 7751da177e4SLinus Torvalds enomem: 7761da177e4SLinus Torvalds printk("Allocation of length %lu from process %d failed\n", 7771da177e4SLinus Torvalds len, current->pid); 7781da177e4SLinus Torvalds show_free_areas(); 7791da177e4SLinus Torvalds return -ENOMEM; 7801da177e4SLinus Torvalds } 7811da177e4SLinus Torvalds 7821da177e4SLinus Torvalds /* 7831da177e4SLinus Torvalds * handle mapping creation for uClinux 7841da177e4SLinus Torvalds */ 7851da177e4SLinus Torvalds unsigned long do_mmap_pgoff(struct file *file, 7861da177e4SLinus Torvalds unsigned long addr, 7871da177e4SLinus Torvalds unsigned long len, 7881da177e4SLinus Torvalds unsigned long prot, 7891da177e4SLinus Torvalds unsigned long flags, 7901da177e4SLinus Torvalds unsigned long pgoff) 7911da177e4SLinus Torvalds { 7921da177e4SLinus Torvalds struct vm_list_struct *vml = NULL; 7931da177e4SLinus Torvalds struct vm_area_struct *vma = NULL; 7941da177e4SLinus Torvalds struct rb_node *rb; 7951da177e4SLinus Torvalds unsigned long capabilities, vm_flags; 7961da177e4SLinus Torvalds void *result; 7971da177e4SLinus Torvalds int ret; 7981da177e4SLinus Torvalds 7991da177e4SLinus Torvalds /* decide whether we should attempt the mapping, and if so what sort of 8001da177e4SLinus Torvalds * mapping */ 8011da177e4SLinus Torvalds ret = validate_mmap_request(file, addr, len, prot, flags, pgoff, 8021da177e4SLinus Torvalds &capabilities); 8031da177e4SLinus Torvalds if (ret < 0) 8041da177e4SLinus Torvalds return ret; 8051da177e4SLinus Torvalds 8061da177e4SLinus Torvalds /* we've determined that we can make the mapping, now translate what we 8071da177e4SLinus Torvalds * now know into VMA flags */ 8081da177e4SLinus Torvalds vm_flags = determine_vm_flags(file, prot, flags, capabilities); 8091da177e4SLinus Torvalds 8101da177e4SLinus Torvalds /* we're going to need to record the mapping if it works */ 8114668edc3SBurman Yan vml = kzalloc(sizeof(struct vm_list_struct), GFP_KERNEL); 8121da177e4SLinus Torvalds if (!vml) 8131da177e4SLinus Torvalds goto error_getting_vml; 8141da177e4SLinus Torvalds 8151da177e4SLinus Torvalds down_write(&nommu_vma_sem); 8161da177e4SLinus Torvalds 8171da177e4SLinus Torvalds /* if we want to share, we need to check for VMAs created by other 8181da177e4SLinus Torvalds * mmap() calls that overlap with our proposed mapping 8191da177e4SLinus Torvalds * - we can only share with an exact match on most regular files 8201da177e4SLinus Torvalds * - shared mappings on character devices and memory backed files are 8211da177e4SLinus Torvalds * permitted to overlap inexactly as far as we are concerned for in 8221da177e4SLinus Torvalds * these cases, sharing is handled in the driver or filesystem rather 8231da177e4SLinus Torvalds * than here 8241da177e4SLinus Torvalds */ 8251da177e4SLinus Torvalds if (vm_flags & VM_MAYSHARE) { 8261da177e4SLinus Torvalds unsigned long pglen = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; 8271da177e4SLinus Torvalds unsigned long vmpglen; 8281da177e4SLinus Torvalds 829*165b2392SDavid Howells /* suppress VMA sharing for shared regions */ 830*165b2392SDavid Howells if (vm_flags & VM_SHARED && 831*165b2392SDavid Howells capabilities & BDI_CAP_MAP_DIRECT) 832*165b2392SDavid Howells goto dont_share_VMAs; 833*165b2392SDavid Howells 8341da177e4SLinus Torvalds for (rb = rb_first(&nommu_vma_tree); rb; rb = rb_next(rb)) { 8351da177e4SLinus Torvalds vma = rb_entry(rb, struct vm_area_struct, vm_rb); 8361da177e4SLinus Torvalds 8371da177e4SLinus Torvalds if (!(vma->vm_flags & VM_MAYSHARE)) 8381da177e4SLinus Torvalds continue; 8391da177e4SLinus Torvalds 8401da177e4SLinus Torvalds /* search for overlapping mappings on the same file */ 841e9536ae7SJosef Sipek if (vma->vm_file->f_path.dentry->d_inode != file->f_path.dentry->d_inode) 8421da177e4SLinus Torvalds continue; 8431da177e4SLinus Torvalds 8441da177e4SLinus Torvalds if (vma->vm_pgoff >= pgoff + pglen) 8451da177e4SLinus Torvalds continue; 8461da177e4SLinus Torvalds 8471da177e4SLinus Torvalds vmpglen = vma->vm_end - vma->vm_start + PAGE_SIZE - 1; 8481da177e4SLinus Torvalds vmpglen >>= PAGE_SHIFT; 8491da177e4SLinus Torvalds if (pgoff >= vma->vm_pgoff + vmpglen) 8501da177e4SLinus Torvalds continue; 8511da177e4SLinus Torvalds 8521da177e4SLinus Torvalds /* handle inexactly overlapping matches between mappings */ 8531da177e4SLinus Torvalds if (vma->vm_pgoff != pgoff || vmpglen != pglen) { 8541da177e4SLinus Torvalds if (!(capabilities & BDI_CAP_MAP_DIRECT)) 8551da177e4SLinus Torvalds goto sharing_violation; 8561da177e4SLinus Torvalds continue; 8571da177e4SLinus Torvalds } 8581da177e4SLinus Torvalds 8591da177e4SLinus Torvalds /* we've found a VMA we can share */ 8601da177e4SLinus Torvalds atomic_inc(&vma->vm_usage); 8611da177e4SLinus Torvalds 8621da177e4SLinus Torvalds vml->vma = vma; 8631da177e4SLinus Torvalds result = (void *) vma->vm_start; 8641da177e4SLinus Torvalds goto shared; 8651da177e4SLinus Torvalds } 8661da177e4SLinus Torvalds 867*165b2392SDavid Howells dont_share_VMAs: 8681da177e4SLinus Torvalds vma = NULL; 8691da177e4SLinus Torvalds 8701da177e4SLinus Torvalds /* obtain the address at which to make a shared mapping 8711da177e4SLinus Torvalds * - this is the hook for quasi-memory character devices to 8721da177e4SLinus Torvalds * tell us the location of a shared mapping 8731da177e4SLinus Torvalds */ 8741da177e4SLinus Torvalds if (file && file->f_op->get_unmapped_area) { 8751da177e4SLinus Torvalds addr = file->f_op->get_unmapped_area(file, addr, len, 8761da177e4SLinus Torvalds pgoff, flags); 8771da177e4SLinus Torvalds if (IS_ERR((void *) addr)) { 8781da177e4SLinus Torvalds ret = addr; 8791da177e4SLinus Torvalds if (ret != (unsigned long) -ENOSYS) 8801da177e4SLinus Torvalds goto error; 8811da177e4SLinus Torvalds 8821da177e4SLinus Torvalds /* the driver refused to tell us where to site 8831da177e4SLinus Torvalds * the mapping so we'll have to attempt to copy 8841da177e4SLinus Torvalds * it */ 8851da177e4SLinus Torvalds ret = (unsigned long) -ENODEV; 8861da177e4SLinus Torvalds if (!(capabilities & BDI_CAP_MAP_COPY)) 8871da177e4SLinus Torvalds goto error; 8881da177e4SLinus Torvalds 8891da177e4SLinus Torvalds capabilities &= ~BDI_CAP_MAP_DIRECT; 8901da177e4SLinus Torvalds } 8911da177e4SLinus Torvalds } 8921da177e4SLinus Torvalds } 8931da177e4SLinus Torvalds 8941da177e4SLinus Torvalds /* we're going to need a VMA struct as well */ 8954668edc3SBurman Yan vma = kzalloc(sizeof(struct vm_area_struct), GFP_KERNEL); 8961da177e4SLinus Torvalds if (!vma) 8971da177e4SLinus Torvalds goto error_getting_vma; 8981da177e4SLinus Torvalds 8991da177e4SLinus Torvalds INIT_LIST_HEAD(&vma->anon_vma_node); 9001da177e4SLinus Torvalds atomic_set(&vma->vm_usage, 1); 9011da177e4SLinus Torvalds if (file) 9021da177e4SLinus Torvalds get_file(file); 9031da177e4SLinus Torvalds vma->vm_file = file; 9041da177e4SLinus Torvalds vma->vm_flags = vm_flags; 9051da177e4SLinus Torvalds vma->vm_start = addr; 9061da177e4SLinus Torvalds vma->vm_end = addr + len; 9071da177e4SLinus Torvalds vma->vm_pgoff = pgoff; 9081da177e4SLinus Torvalds 9091da177e4SLinus Torvalds vml->vma = vma; 9101da177e4SLinus Torvalds 9111da177e4SLinus Torvalds /* set up the mapping */ 9121da177e4SLinus Torvalds if (file && vma->vm_flags & VM_SHARED) 9131da177e4SLinus Torvalds ret = do_mmap_shared_file(vma, len); 9141da177e4SLinus Torvalds else 9151da177e4SLinus Torvalds ret = do_mmap_private(vma, len); 9161da177e4SLinus Torvalds if (ret < 0) 9171da177e4SLinus Torvalds goto error; 9181da177e4SLinus Torvalds 9191da177e4SLinus Torvalds /* okay... we have a mapping; now we have to register it */ 9201da177e4SLinus Torvalds result = (void *) vma->vm_start; 9211da177e4SLinus Torvalds 9221da177e4SLinus Torvalds if (vma->vm_flags & VM_MAPPED_COPY) { 9231da177e4SLinus Torvalds realalloc += kobjsize(result); 9241da177e4SLinus Torvalds askedalloc += len; 9251da177e4SLinus Torvalds } 9261da177e4SLinus Torvalds 9271da177e4SLinus Torvalds realalloc += kobjsize(vma); 9281da177e4SLinus Torvalds askedalloc += sizeof(*vma); 9291da177e4SLinus Torvalds 9301da177e4SLinus Torvalds current->mm->total_vm += len >> PAGE_SHIFT; 9311da177e4SLinus Torvalds 9321da177e4SLinus Torvalds add_nommu_vma(vma); 9331da177e4SLinus Torvalds 9341da177e4SLinus Torvalds shared: 9351da177e4SLinus Torvalds realalloc += kobjsize(vml); 9361da177e4SLinus Torvalds askedalloc += sizeof(*vml); 9371da177e4SLinus Torvalds 9383034097aSDavid Howells add_vma_to_mm(current->mm, vml); 9391da177e4SLinus Torvalds 9401da177e4SLinus Torvalds up_write(&nommu_vma_sem); 9411da177e4SLinus Torvalds 9421da177e4SLinus Torvalds if (prot & PROT_EXEC) 9431da177e4SLinus Torvalds flush_icache_range((unsigned long) result, 9441da177e4SLinus Torvalds (unsigned long) result + len); 9451da177e4SLinus Torvalds 9461da177e4SLinus Torvalds #ifdef DEBUG 9471da177e4SLinus Torvalds printk("do_mmap:\n"); 9481da177e4SLinus Torvalds show_process_blocks(); 9491da177e4SLinus Torvalds #endif 9501da177e4SLinus Torvalds 9511da177e4SLinus Torvalds return (unsigned long) result; 9521da177e4SLinus Torvalds 9531da177e4SLinus Torvalds error: 9541da177e4SLinus Torvalds up_write(&nommu_vma_sem); 9551da177e4SLinus Torvalds kfree(vml); 9561da177e4SLinus Torvalds if (vma) { 9573fcd03e0SGavin Lambert if (vma->vm_file) 9581da177e4SLinus Torvalds fput(vma->vm_file); 9591da177e4SLinus Torvalds kfree(vma); 9601da177e4SLinus Torvalds } 9611da177e4SLinus Torvalds return ret; 9621da177e4SLinus Torvalds 9631da177e4SLinus Torvalds sharing_violation: 9641da177e4SLinus Torvalds up_write(&nommu_vma_sem); 9651da177e4SLinus Torvalds printk("Attempt to share mismatched mappings\n"); 9661da177e4SLinus Torvalds kfree(vml); 9671da177e4SLinus Torvalds return -EINVAL; 9681da177e4SLinus Torvalds 9691da177e4SLinus Torvalds error_getting_vma: 9701da177e4SLinus Torvalds up_write(&nommu_vma_sem); 9711da177e4SLinus Torvalds kfree(vml); 97266aa2b4bSGreg Ungerer printk("Allocation of vma for %lu byte allocation from process %d failed\n", 9731da177e4SLinus Torvalds len, current->pid); 9741da177e4SLinus Torvalds show_free_areas(); 9751da177e4SLinus Torvalds return -ENOMEM; 9761da177e4SLinus Torvalds 9771da177e4SLinus Torvalds error_getting_vml: 9781da177e4SLinus Torvalds printk("Allocation of vml for %lu byte allocation from process %d failed\n", 9791da177e4SLinus Torvalds len, current->pid); 9801da177e4SLinus Torvalds show_free_areas(); 9811da177e4SLinus Torvalds return -ENOMEM; 9821da177e4SLinus Torvalds } 9831da177e4SLinus Torvalds 9841da177e4SLinus Torvalds /* 9851da177e4SLinus Torvalds * handle mapping disposal for uClinux 9861da177e4SLinus Torvalds */ 9871da177e4SLinus Torvalds static void put_vma(struct vm_area_struct *vma) 9881da177e4SLinus Torvalds { 9891da177e4SLinus Torvalds if (vma) { 9901da177e4SLinus Torvalds down_write(&nommu_vma_sem); 9911da177e4SLinus Torvalds 9921da177e4SLinus Torvalds if (atomic_dec_and_test(&vma->vm_usage)) { 9931da177e4SLinus Torvalds delete_nommu_vma(vma); 9941da177e4SLinus Torvalds 9951da177e4SLinus Torvalds if (vma->vm_ops && vma->vm_ops->close) 9961da177e4SLinus Torvalds vma->vm_ops->close(vma); 9971da177e4SLinus Torvalds 9981da177e4SLinus Torvalds /* IO memory and memory shared directly out of the pagecache from 9991da177e4SLinus Torvalds * ramfs/tmpfs mustn't be released here */ 10001da177e4SLinus Torvalds if (vma->vm_flags & VM_MAPPED_COPY) { 10011da177e4SLinus Torvalds realalloc -= kobjsize((void *) vma->vm_start); 10021da177e4SLinus Torvalds askedalloc -= vma->vm_end - vma->vm_start; 10031da177e4SLinus Torvalds kfree((void *) vma->vm_start); 10041da177e4SLinus Torvalds } 10051da177e4SLinus Torvalds 10061da177e4SLinus Torvalds realalloc -= kobjsize(vma); 10071da177e4SLinus Torvalds askedalloc -= sizeof(*vma); 10081da177e4SLinus Torvalds 10091da177e4SLinus Torvalds if (vma->vm_file) 10101da177e4SLinus Torvalds fput(vma->vm_file); 10111da177e4SLinus Torvalds kfree(vma); 10121da177e4SLinus Torvalds } 10131da177e4SLinus Torvalds 10141da177e4SLinus Torvalds up_write(&nommu_vma_sem); 10151da177e4SLinus Torvalds } 10161da177e4SLinus Torvalds } 10171da177e4SLinus Torvalds 10183034097aSDavid Howells /* 10193034097aSDavid Howells * release a mapping 10203034097aSDavid Howells * - under NOMMU conditions the parameters must match exactly to the mapping to 10213034097aSDavid Howells * be removed 10223034097aSDavid Howells */ 10231da177e4SLinus Torvalds int do_munmap(struct mm_struct *mm, unsigned long addr, size_t len) 10241da177e4SLinus Torvalds { 10251da177e4SLinus Torvalds struct vm_list_struct *vml, **parent; 10261da177e4SLinus Torvalds unsigned long end = addr + len; 10271da177e4SLinus Torvalds 10281da177e4SLinus Torvalds #ifdef DEBUG 10291da177e4SLinus Torvalds printk("do_munmap:\n"); 10301da177e4SLinus Torvalds #endif 10311da177e4SLinus Torvalds 10323034097aSDavid Howells for (parent = &mm->context.vmlist; *parent; parent = &(*parent)->next) { 10333034097aSDavid Howells if ((*parent)->vma->vm_start > addr) 10343034097aSDavid Howells break; 10351da177e4SLinus Torvalds if ((*parent)->vma->vm_start == addr && 103666aa2b4bSGreg Ungerer ((len == 0) || ((*parent)->vma->vm_end == end))) 10371da177e4SLinus Torvalds goto found; 10383034097aSDavid Howells } 10391da177e4SLinus Torvalds 10401da177e4SLinus Torvalds printk("munmap of non-mmaped memory by process %d (%s): %p\n", 10411da177e4SLinus Torvalds current->pid, current->comm, (void *) addr); 10421da177e4SLinus Torvalds return -EINVAL; 10431da177e4SLinus Torvalds 10441da177e4SLinus Torvalds found: 10451da177e4SLinus Torvalds vml = *parent; 10461da177e4SLinus Torvalds 10471da177e4SLinus Torvalds put_vma(vml->vma); 10481da177e4SLinus Torvalds 10491da177e4SLinus Torvalds *parent = vml->next; 10501da177e4SLinus Torvalds realalloc -= kobjsize(vml); 10511da177e4SLinus Torvalds askedalloc -= sizeof(*vml); 10521da177e4SLinus Torvalds kfree(vml); 1053365e9c87SHugh Dickins 1054365e9c87SHugh Dickins update_hiwater_vm(mm); 10551da177e4SLinus Torvalds mm->total_vm -= len >> PAGE_SHIFT; 10561da177e4SLinus Torvalds 10571da177e4SLinus Torvalds #ifdef DEBUG 10581da177e4SLinus Torvalds show_process_blocks(); 10591da177e4SLinus Torvalds #endif 10601da177e4SLinus Torvalds 10611da177e4SLinus Torvalds return 0; 10621da177e4SLinus Torvalds } 10631da177e4SLinus Torvalds 10643034097aSDavid Howells asmlinkage long sys_munmap(unsigned long addr, size_t len) 10653034097aSDavid Howells { 10663034097aSDavid Howells int ret; 10673034097aSDavid Howells struct mm_struct *mm = current->mm; 10683034097aSDavid Howells 10693034097aSDavid Howells down_write(&mm->mmap_sem); 10703034097aSDavid Howells ret = do_munmap(mm, addr, len); 10713034097aSDavid Howells up_write(&mm->mmap_sem); 10723034097aSDavid Howells return ret; 10733034097aSDavid Howells } 10743034097aSDavid Howells 10753034097aSDavid Howells /* 10763034097aSDavid Howells * Release all mappings 10773034097aSDavid Howells */ 10781da177e4SLinus Torvalds void exit_mmap(struct mm_struct * mm) 10791da177e4SLinus Torvalds { 10801da177e4SLinus Torvalds struct vm_list_struct *tmp; 10811da177e4SLinus Torvalds 10821da177e4SLinus Torvalds if (mm) { 10831da177e4SLinus Torvalds #ifdef DEBUG 10841da177e4SLinus Torvalds printk("Exit_mmap:\n"); 10851da177e4SLinus Torvalds #endif 10861da177e4SLinus Torvalds 10871da177e4SLinus Torvalds mm->total_vm = 0; 10881da177e4SLinus Torvalds 10891da177e4SLinus Torvalds while ((tmp = mm->context.vmlist)) { 10901da177e4SLinus Torvalds mm->context.vmlist = tmp->next; 10911da177e4SLinus Torvalds put_vma(tmp->vma); 10921da177e4SLinus Torvalds 10931da177e4SLinus Torvalds realalloc -= kobjsize(tmp); 10941da177e4SLinus Torvalds askedalloc -= sizeof(*tmp); 10951da177e4SLinus Torvalds kfree(tmp); 10961da177e4SLinus Torvalds } 10971da177e4SLinus Torvalds 10981da177e4SLinus Torvalds #ifdef DEBUG 10991da177e4SLinus Torvalds show_process_blocks(); 11001da177e4SLinus Torvalds #endif 11011da177e4SLinus Torvalds } 11021da177e4SLinus Torvalds } 11031da177e4SLinus Torvalds 11041da177e4SLinus Torvalds unsigned long do_brk(unsigned long addr, unsigned long len) 11051da177e4SLinus Torvalds { 11061da177e4SLinus Torvalds return -ENOMEM; 11071da177e4SLinus Torvalds } 11081da177e4SLinus Torvalds 11091da177e4SLinus Torvalds /* 11106fa5f80bSDavid Howells * expand (or shrink) an existing mapping, potentially moving it at the same 11116fa5f80bSDavid Howells * time (controlled by the MREMAP_MAYMOVE flag and available VM space) 11121da177e4SLinus Torvalds * 11136fa5f80bSDavid Howells * under NOMMU conditions, we only permit changing a mapping's size, and only 11146fa5f80bSDavid Howells * as long as it stays within the hole allocated by the kmalloc() call in 11156fa5f80bSDavid Howells * do_mmap_pgoff() and the block is not shareable 11161da177e4SLinus Torvalds * 11176fa5f80bSDavid Howells * MREMAP_FIXED is not supported under NOMMU conditions 11181da177e4SLinus Torvalds */ 11191da177e4SLinus Torvalds unsigned long do_mremap(unsigned long addr, 11201da177e4SLinus Torvalds unsigned long old_len, unsigned long new_len, 11211da177e4SLinus Torvalds unsigned long flags, unsigned long new_addr) 11221da177e4SLinus Torvalds { 11236fa5f80bSDavid Howells struct vm_area_struct *vma; 11241da177e4SLinus Torvalds 11251da177e4SLinus Torvalds /* insanity checks first */ 11261da177e4SLinus Torvalds if (new_len == 0) 11271da177e4SLinus Torvalds return (unsigned long) -EINVAL; 11281da177e4SLinus Torvalds 11291da177e4SLinus Torvalds if (flags & MREMAP_FIXED && new_addr != addr) 11301da177e4SLinus Torvalds return (unsigned long) -EINVAL; 11311da177e4SLinus Torvalds 11326fa5f80bSDavid Howells vma = find_vma_exact(current->mm, addr); 11336fa5f80bSDavid Howells if (!vma) 11341da177e4SLinus Torvalds return (unsigned long) -EINVAL; 11351da177e4SLinus Torvalds 11366fa5f80bSDavid Howells if (vma->vm_end != vma->vm_start + old_len) 11371da177e4SLinus Torvalds return (unsigned long) -EFAULT; 11381da177e4SLinus Torvalds 11396fa5f80bSDavid Howells if (vma->vm_flags & VM_MAYSHARE) 11401da177e4SLinus Torvalds return (unsigned long) -EPERM; 11411da177e4SLinus Torvalds 11421da177e4SLinus Torvalds if (new_len > kobjsize((void *) addr)) 11431da177e4SLinus Torvalds return (unsigned long) -ENOMEM; 11441da177e4SLinus Torvalds 11451da177e4SLinus Torvalds /* all checks complete - do it */ 11466fa5f80bSDavid Howells vma->vm_end = vma->vm_start + new_len; 11471da177e4SLinus Torvalds 11481da177e4SLinus Torvalds askedalloc -= old_len; 11491da177e4SLinus Torvalds askedalloc += new_len; 11501da177e4SLinus Torvalds 11516fa5f80bSDavid Howells return vma->vm_start; 11526fa5f80bSDavid Howells } 11536fa5f80bSDavid Howells 11546fa5f80bSDavid Howells asmlinkage unsigned long sys_mremap(unsigned long addr, 11556fa5f80bSDavid Howells unsigned long old_len, unsigned long new_len, 11566fa5f80bSDavid Howells unsigned long flags, unsigned long new_addr) 11576fa5f80bSDavid Howells { 11586fa5f80bSDavid Howells unsigned long ret; 11596fa5f80bSDavid Howells 11606fa5f80bSDavid Howells down_write(¤t->mm->mmap_sem); 11616fa5f80bSDavid Howells ret = do_mremap(addr, old_len, new_len, flags, new_addr); 11626fa5f80bSDavid Howells up_write(¤t->mm->mmap_sem); 11636fa5f80bSDavid Howells return ret; 11641da177e4SLinus Torvalds } 11651da177e4SLinus Torvalds 11666aab341eSLinus Torvalds struct page *follow_page(struct vm_area_struct *vma, unsigned long address, 1167deceb6cdSHugh Dickins unsigned int foll_flags) 11681da177e4SLinus Torvalds { 11691da177e4SLinus Torvalds return NULL; 11701da177e4SLinus Torvalds } 11711da177e4SLinus Torvalds 11721da177e4SLinus Torvalds int remap_pfn_range(struct vm_area_struct *vma, unsigned long from, 11731da177e4SLinus Torvalds unsigned long to, unsigned long size, pgprot_t prot) 11741da177e4SLinus Torvalds { 117566aa2b4bSGreg Ungerer vma->vm_start = vma->vm_pgoff << PAGE_SHIFT; 117666aa2b4bSGreg Ungerer return 0; 11771da177e4SLinus Torvalds } 117822c4af40SLuke Yang EXPORT_SYMBOL(remap_pfn_range); 11791da177e4SLinus Torvalds 11801da177e4SLinus Torvalds void swap_unplug_io_fn(struct backing_dev_info *bdi, struct page *page) 11811da177e4SLinus Torvalds { 11821da177e4SLinus Torvalds } 11831da177e4SLinus Torvalds 11841da177e4SLinus Torvalds unsigned long arch_get_unmapped_area(struct file *file, unsigned long addr, 11851da177e4SLinus Torvalds unsigned long len, unsigned long pgoff, unsigned long flags) 11861da177e4SLinus Torvalds { 11871da177e4SLinus Torvalds return -ENOMEM; 11881da177e4SLinus Torvalds } 11891da177e4SLinus Torvalds 11901363c3cdSWolfgang Wander void arch_unmap_area(struct mm_struct *mm, unsigned long addr) 11911da177e4SLinus Torvalds { 11921da177e4SLinus Torvalds } 11931da177e4SLinus Torvalds 11941da177e4SLinus Torvalds void unmap_mapping_range(struct address_space *mapping, 11951da177e4SLinus Torvalds loff_t const holebegin, loff_t const holelen, 11961da177e4SLinus Torvalds int even_cows) 11971da177e4SLinus Torvalds { 11981da177e4SLinus Torvalds } 119922c4af40SLuke Yang EXPORT_SYMBOL(unmap_mapping_range); 12001da177e4SLinus Torvalds 12011da177e4SLinus Torvalds /* 1202d56e03cdSDavid Howells * ask for an unmapped area at which to create a mapping on a file 1203d56e03cdSDavid Howells */ 1204d56e03cdSDavid Howells unsigned long get_unmapped_area(struct file *file, unsigned long addr, 1205d56e03cdSDavid Howells unsigned long len, unsigned long pgoff, 1206d56e03cdSDavid Howells unsigned long flags) 1207d56e03cdSDavid Howells { 1208d56e03cdSDavid Howells unsigned long (*get_area)(struct file *, unsigned long, unsigned long, 1209d56e03cdSDavid Howells unsigned long, unsigned long); 1210d56e03cdSDavid Howells 1211d56e03cdSDavid Howells get_area = current->mm->get_unmapped_area; 1212d56e03cdSDavid Howells if (file && file->f_op && file->f_op->get_unmapped_area) 1213d56e03cdSDavid Howells get_area = file->f_op->get_unmapped_area; 1214d56e03cdSDavid Howells 1215d56e03cdSDavid Howells if (!get_area) 1216d56e03cdSDavid Howells return -ENOSYS; 1217d56e03cdSDavid Howells 1218d56e03cdSDavid Howells return get_area(file, addr, len, pgoff, flags); 1219d56e03cdSDavid Howells } 1220d56e03cdSDavid Howells 1221d56e03cdSDavid Howells EXPORT_SYMBOL(get_unmapped_area); 1222d56e03cdSDavid Howells 1223d56e03cdSDavid Howells /* 12241da177e4SLinus Torvalds * Check that a process has enough memory to allocate a new virtual 12251da177e4SLinus Torvalds * mapping. 0 means there is enough memory for the allocation to 12261da177e4SLinus Torvalds * succeed and -ENOMEM implies there is not. 12271da177e4SLinus Torvalds * 12281da177e4SLinus Torvalds * We currently support three overcommit policies, which are set via the 12291da177e4SLinus Torvalds * vm.overcommit_memory sysctl. See Documentation/vm/overcommit-accounting 12301da177e4SLinus Torvalds * 12311da177e4SLinus Torvalds * Strict overcommit modes added 2002 Feb 26 by Alan Cox. 12321da177e4SLinus Torvalds * Additional code 2002 Jul 20 by Robert Love. 12331da177e4SLinus Torvalds * 12341da177e4SLinus Torvalds * cap_sys_admin is 1 if the process has admin privileges, 0 otherwise. 12351da177e4SLinus Torvalds * 12361da177e4SLinus Torvalds * Note this is a helper function intended to be used by LSMs which 12371da177e4SLinus Torvalds * wish to use this logic. 12381da177e4SLinus Torvalds */ 12391da177e4SLinus Torvalds int __vm_enough_memory(long pages, int cap_sys_admin) 12401da177e4SLinus Torvalds { 12411da177e4SLinus Torvalds unsigned long free, allowed; 12421da177e4SLinus Torvalds 12431da177e4SLinus Torvalds vm_acct_memory(pages); 12441da177e4SLinus Torvalds 12451da177e4SLinus Torvalds /* 12461da177e4SLinus Torvalds * Sometimes we want to use more memory than we have 12471da177e4SLinus Torvalds */ 12481da177e4SLinus Torvalds if (sysctl_overcommit_memory == OVERCOMMIT_ALWAYS) 12491da177e4SLinus Torvalds return 0; 12501da177e4SLinus Torvalds 12511da177e4SLinus Torvalds if (sysctl_overcommit_memory == OVERCOMMIT_GUESS) { 12521da177e4SLinus Torvalds unsigned long n; 12531da177e4SLinus Torvalds 1254347ce434SChristoph Lameter free = global_page_state(NR_FILE_PAGES); 12551da177e4SLinus Torvalds free += nr_swap_pages; 12561da177e4SLinus Torvalds 12571da177e4SLinus Torvalds /* 12581da177e4SLinus Torvalds * Any slabs which are created with the 12591da177e4SLinus Torvalds * SLAB_RECLAIM_ACCOUNT flag claim to have contents 12601da177e4SLinus Torvalds * which are reclaimable, under pressure. The dentry 12611da177e4SLinus Torvalds * cache and most inode caches should fall into this 12621da177e4SLinus Torvalds */ 1263972d1a7bSChristoph Lameter free += global_page_state(NR_SLAB_RECLAIMABLE); 12641da177e4SLinus Torvalds 12651da177e4SLinus Torvalds /* 12661da177e4SLinus Torvalds * Leave the last 3% for root 12671da177e4SLinus Torvalds */ 12681da177e4SLinus Torvalds if (!cap_sys_admin) 12691da177e4SLinus Torvalds free -= free / 32; 12701da177e4SLinus Torvalds 12711da177e4SLinus Torvalds if (free > pages) 12721da177e4SLinus Torvalds return 0; 12731da177e4SLinus Torvalds 12741da177e4SLinus Torvalds /* 12751da177e4SLinus Torvalds * nr_free_pages() is very expensive on large systems, 12761da177e4SLinus Torvalds * only call if we're about to fail. 12771da177e4SLinus Torvalds */ 12781da177e4SLinus Torvalds n = nr_free_pages(); 1279d5ddc79bSHideo AOKI 1280d5ddc79bSHideo AOKI /* 1281d5ddc79bSHideo AOKI * Leave reserved pages. The pages are not for anonymous pages. 1282d5ddc79bSHideo AOKI */ 1283d5ddc79bSHideo AOKI if (n <= totalreserve_pages) 1284d5ddc79bSHideo AOKI goto error; 1285d5ddc79bSHideo AOKI else 1286d5ddc79bSHideo AOKI n -= totalreserve_pages; 1287d5ddc79bSHideo AOKI 1288d5ddc79bSHideo AOKI /* 1289d5ddc79bSHideo AOKI * Leave the last 3% for root 1290d5ddc79bSHideo AOKI */ 12911da177e4SLinus Torvalds if (!cap_sys_admin) 12921da177e4SLinus Torvalds n -= n / 32; 12931da177e4SLinus Torvalds free += n; 12941da177e4SLinus Torvalds 12951da177e4SLinus Torvalds if (free > pages) 12961da177e4SLinus Torvalds return 0; 1297d5ddc79bSHideo AOKI 1298d5ddc79bSHideo AOKI goto error; 12991da177e4SLinus Torvalds } 13001da177e4SLinus Torvalds 13011da177e4SLinus Torvalds allowed = totalram_pages * sysctl_overcommit_ratio / 100; 13021da177e4SLinus Torvalds /* 13031da177e4SLinus Torvalds * Leave the last 3% for root 13041da177e4SLinus Torvalds */ 13051da177e4SLinus Torvalds if (!cap_sys_admin) 13061da177e4SLinus Torvalds allowed -= allowed / 32; 13071da177e4SLinus Torvalds allowed += total_swap_pages; 13081da177e4SLinus Torvalds 13091da177e4SLinus Torvalds /* Don't let a single process grow too big: 13101da177e4SLinus Torvalds leave 3% of the size of this process for other processes */ 13111da177e4SLinus Torvalds allowed -= current->mm->total_vm / 32; 13121da177e4SLinus Torvalds 13132f60f8d3SSimon Derr /* 13142f60f8d3SSimon Derr * cast `allowed' as a signed long because vm_committed_space 13152f60f8d3SSimon Derr * sometimes has a negative value 13162f60f8d3SSimon Derr */ 13172f60f8d3SSimon Derr if (atomic_read(&vm_committed_space) < (long)allowed) 13181da177e4SLinus Torvalds return 0; 1319d5ddc79bSHideo AOKI error: 13201da177e4SLinus Torvalds vm_unacct_memory(pages); 13211da177e4SLinus Torvalds 13221da177e4SLinus Torvalds return -ENOMEM; 13231da177e4SLinus Torvalds } 13241da177e4SLinus Torvalds 13251da177e4SLinus Torvalds int in_gate_area_no_task(unsigned long addr) 13261da177e4SLinus Torvalds { 13271da177e4SLinus Torvalds return 0; 13281da177e4SLinus Torvalds } 1329b0e15190SDavid Howells 1330b0e15190SDavid Howells struct page *filemap_nopage(struct vm_area_struct *area, 1331b0e15190SDavid Howells unsigned long address, int *type) 1332b0e15190SDavid Howells { 1333b0e15190SDavid Howells BUG(); 1334b0e15190SDavid Howells return NULL; 1335b0e15190SDavid Howells } 13360ec76a11SDavid Howells 13370ec76a11SDavid Howells /* 13380ec76a11SDavid Howells * Access another process' address space. 13390ec76a11SDavid Howells * - source/target buffer must be kernel space 13400ec76a11SDavid Howells */ 13410ec76a11SDavid Howells int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write) 13420ec76a11SDavid Howells { 13430ec76a11SDavid Howells struct vm_area_struct *vma; 13440ec76a11SDavid Howells struct mm_struct *mm; 13450ec76a11SDavid Howells 13460ec76a11SDavid Howells if (addr + len < addr) 13470ec76a11SDavid Howells return 0; 13480ec76a11SDavid Howells 13490ec76a11SDavid Howells mm = get_task_mm(tsk); 13500ec76a11SDavid Howells if (!mm) 13510ec76a11SDavid Howells return 0; 13520ec76a11SDavid Howells 13530ec76a11SDavid Howells down_read(&mm->mmap_sem); 13540ec76a11SDavid Howells 13550ec76a11SDavid Howells /* the access must start within one of the target process's mappings */ 13560159b141SDavid Howells vma = find_vma(mm, addr); 13570159b141SDavid Howells if (vma) { 13580ec76a11SDavid Howells /* don't overrun this mapping */ 13590ec76a11SDavid Howells if (addr + len >= vma->vm_end) 13600ec76a11SDavid Howells len = vma->vm_end - addr; 13610ec76a11SDavid Howells 13620ec76a11SDavid Howells /* only read or write mappings where it is permitted */ 1363d00c7b99SDavid Howells if (write && vma->vm_flags & VM_MAYWRITE) 13640ec76a11SDavid Howells len -= copy_to_user((void *) addr, buf, len); 1365d00c7b99SDavid Howells else if (!write && vma->vm_flags & VM_MAYREAD) 13660ec76a11SDavid Howells len -= copy_from_user(buf, (void *) addr, len); 13670ec76a11SDavid Howells else 13680ec76a11SDavid Howells len = 0; 13690ec76a11SDavid Howells } else { 13700ec76a11SDavid Howells len = 0; 13710ec76a11SDavid Howells } 13720ec76a11SDavid Howells 13730ec76a11SDavid Howells up_read(&mm->mmap_sem); 13740ec76a11SDavid Howells mmput(mm); 13750ec76a11SDavid Howells return len; 13760ec76a11SDavid Howells } 1377