/* * linux/mm/mlock.c * * (C) Copyright 1995 Linus Torvalds */ #include <linux/slab.h> #include <linux/shm.h> #include <linux/mman.h> #include <linux/smp_lock.h> #include <linux/pagemap.h> #include <asm/uaccess.h> #include <asm/pgtable.h> 15 static inline int mlock_fixup_all(struct vm_area_struct * vma, int newflags) { spin_lock(&vma->vm_mm->page_table_lock); vma->vm_flags = newflags; 19 spin_unlock(&vma->vm_mm->page_table_lock); 20 return 0; } 23 static inline int mlock_fixup_start(struct vm_area_struct * vma, unsigned long end, int newflags) { struct vm_area_struct * n; n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); 29 if (!n) 30 return -EAGAIN; *n = *vma; n->vm_end = end; n->vm_flags = newflags; n->vm_raend = 0; 35 if (n->vm_file) get_file(n->vm_file); 37 if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); lock_vma_mappings(vma); spin_lock(&vma->vm_mm->page_table_lock); vma->vm_pgoff += (end - vma->vm_start) >> PAGE_SHIFT; vma->vm_start = end; __insert_vm_struct(current->mm, n); 44 spin_unlock(&vma->vm_mm->page_table_lock); unlock_vma_mappings(vma); 46 return 0; } 49 static inline int mlock_fixup_end(struct vm_area_struct * vma, unsigned long start, int newflags) { struct vm_area_struct * n; n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); 55 if (!n) 56 return -EAGAIN; *n = *vma; n->vm_start = start; n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT; n->vm_flags = newflags; n->vm_raend = 0; 62 if (n->vm_file) get_file(n->vm_file); 64 if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); lock_vma_mappings(vma); spin_lock(&vma->vm_mm->page_table_lock); vma->vm_end = start; __insert_vm_struct(current->mm, n); 70 spin_unlock(&vma->vm_mm->page_table_lock); unlock_vma_mappings(vma); 72 return 0; } 75 static inline int mlock_fixup_middle(struct vm_area_struct * vma, unsigned long start, unsigned long end, int newflags) { struct vm_area_struct * left, * right; left = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); 81 if (!left) 82 return -EAGAIN; right = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); 84 if (!right) { kmem_cache_free(vm_area_cachep, left); 86 return -EAGAIN; } *left = *vma; *right = *vma; left->vm_end = start; right->vm_start = end; right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT; vma->vm_flags = newflags; left->vm_raend = 0; right->vm_raend = 0; 96 if (vma->vm_file) atomic_add(2, &vma->vm_file->f_count); 99 if (vma->vm_ops && vma->vm_ops->open) { vma->vm_ops->open(left); vma->vm_ops->open(right); } lock_vma_mappings(vma); spin_lock(&vma->vm_mm->page_table_lock); vma->vm_pgoff += (start - vma->vm_start) >> PAGE_SHIFT; vma->vm_start = start; vma->vm_end = end; vma->vm_flags = newflags; vma->vm_raend = 0; __insert_vm_struct(current->mm, left); __insert_vm_struct(current->mm, right); 112 spin_unlock(&vma->vm_mm->page_table_lock); unlock_vma_mappings(vma); 114 return 0; } 117 static int mlock_fixup(struct vm_area_struct * vma, unsigned long start, unsigned long end, unsigned int newflags) { int pages, retval; 122 if (newflags == vma->vm_flags) 123 return 0; 125 if (start == vma->vm_start) { 126 if (end == vma->vm_end) retval = mlock_fixup_all(vma, newflags); 128 else retval = mlock_fixup_start(vma, end, newflags); 130 } else { 131 if (end == vma->vm_end) retval = mlock_fixup_end(vma, start, newflags); 133 else retval = mlock_fixup_middle(vma, start, end, newflags); } 136 if (!retval) { /* keep track of amount of locked VM */ pages = (end - start) >> PAGE_SHIFT; 139 if (newflags & VM_LOCKED) { pages = -pages; make_pages_present(start, end); } vma->vm_mm->locked_vm -= pages; } 145 return retval; } 148 static int do_mlock(unsigned long start, size_t len, int on) { unsigned long nstart, end, tmp; struct vm_area_struct * vma, * next; int error; 154 if (on && !capable(CAP_IPC_LOCK)) 155 return -EPERM; len = PAGE_ALIGN(len); end = start + len; 158 if (end < start) 159 return -EINVAL; 160 if (end == start) 161 return 0; vma = find_vma(current->mm, start); 163 if (!vma || vma->vm_start > start) 164 return -ENOMEM; 166 for (nstart = start ; ; ) { unsigned int newflags; /* Here we know that vma->vm_start <= nstart < vma->vm_end. */ newflags = vma->vm_flags | VM_LOCKED; 172 if (!on) newflags &= ~VM_LOCKED; 175 if (vma->vm_end >= end) { error = mlock_fixup(vma, nstart, end, newflags); 177 break; } tmp = vma->vm_end; next = vma->vm_next; error = mlock_fixup(vma, nstart, tmp, newflags); 183 if (error) 184 break; nstart = tmp; vma = next; 187 if (!vma || vma->vm_start != nstart) { error = -ENOMEM; 189 break; } } 192 return error; } 195 asmlinkage long sys_mlock(unsigned long start, size_t len) { unsigned long locked; unsigned long lock_limit; int error = -ENOMEM; down(¤t->mm->mmap_sem); len = PAGE_ALIGN(len + (start & ~PAGE_MASK)); start &= PAGE_MASK; locked = len >> PAGE_SHIFT; locked += current->mm->locked_vm; lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur; lock_limit >>= PAGE_SHIFT; /* check against resource limits */ 212 if (locked > lock_limit) 213 goto out; /* we may lock at most half of physical memory... */ /* (this check is pretty bogus, but doesn't hurt) */ 217 if (locked > num_physpages/2) 218 goto out; error = do_mlock(start, len, 1); out: up(¤t->mm->mmap_sem); 223 return error; } 226 asmlinkage long sys_munlock(unsigned long start, size_t len) { int ret; down(¤t->mm->mmap_sem); len = PAGE_ALIGN(len + (start & ~PAGE_MASK)); start &= PAGE_MASK; ret = do_mlock(start, len, 0); up(¤t->mm->mmap_sem); 235 return ret; } 238 static int do_mlockall(int flags) { int error; unsigned int def_flags; struct vm_area_struct * vma; 244 if (!capable(CAP_IPC_LOCK)) 245 return -EPERM; def_flags = 0; 248 if (flags & MCL_FUTURE) def_flags = VM_LOCKED; current->mm->def_flags = def_flags; error = 0; 253 for (vma = current->mm->mmap; vma ; vma = vma->vm_next) { unsigned int newflags; newflags = vma->vm_flags | VM_LOCKED; 257 if (!(flags & MCL_CURRENT)) newflags &= ~VM_LOCKED; error = mlock_fixup(vma, vma->vm_start, vma->vm_end, newflags); 260 if (error) 261 break; } 263 return error; } 266 asmlinkage long sys_mlockall(int flags) { unsigned long lock_limit; int ret = -EINVAL; down(¤t->mm->mmap_sem); 272 if (!flags || (flags & ~(MCL_CURRENT | MCL_FUTURE))) 273 goto out; lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur; lock_limit >>= PAGE_SHIFT; ret = -ENOMEM; 279 if (current->mm->total_vm > lock_limit) 280 goto out; /* we may lock at most half of physical memory... */ /* (this check is pretty bogus, but doesn't hurt) */ 284 if (current->mm->total_vm > num_physpages/2) 285 goto out; ret = do_mlockall(flags); out: up(¤t->mm->mmap_sem); 290 return ret; } 293 asmlinkage long sys_munlockall(void) { int ret; down(¤t->mm->mmap_sem); ret = do_mlockall(0); up(¤t->mm->mmap_sem); 300 return ret; }