RHEL4/mm/mlock.c
<<
>>
Prefs
   1/*
   2 *      linux/mm/mlock.c
   3 *
   4 *  (C) Copyright 1995 Linus Torvalds
   5 *  (C) Copyright 2002 Christoph Hellwig
   6 */
   7
   8#include <linux/mman.h>
   9#include <linux/mm.h>
  10
  11
  12static int mlock_fixup(struct vm_area_struct * vma, 
  13        unsigned long start, unsigned long end, unsigned int newflags)
  14{
  15        struct mm_struct * mm = vma->vm_mm;
  16        int pages;
  17        int ret = 0;
  18
  19        if (newflags == vma->vm_flags)
  20                goto out;
  21
  22        if (start != vma->vm_start) {
  23                ret = split_vma(mm, vma, start, 1);
  24                if (ret)
  25                        goto out;
  26        }
  27
  28        if (end != vma->vm_end) {
  29                ret = split_vma(mm, vma, end, 0);
  30                if (ret)
  31                        goto out;
  32        }
  33
  34        /*
  35         * vm_flags is protected by the mmap_sem held in write mode.
  36         * It's okay if try_to_unmap_one unmaps a page just after we
  37         * set VM_LOCKED, make_pages_present below will bring it back.
  38         */
  39        vma->vm_flags = newflags;
  40
  41        /*
  42         * Keep track of amount of locked VM.
  43         */
  44        pages = (end - start) >> PAGE_SHIFT;
  45        if (newflags & VM_LOCKED) {
  46                pages = -pages;
  47                if (!(newflags & VM_IO))
  48                        ret = make_pages_present(start, end);
  49        }
  50
  51        vma->vm_mm->locked_vm -= pages;
  52out:
  53        if (ret == -ENOMEM)
  54                ret = -EAGAIN;
  55        return ret;
  56}
  57
  58static int do_mlock(unsigned long start, size_t len, int on)
  59{
  60        unsigned long nstart, end, tmp;
  61        struct vm_area_struct * vma, * next;
  62        int error;
  63
  64        len = PAGE_ALIGN(len);
  65        end = start + len;
  66        if (end < start)
  67                return -EINVAL;
  68        if (end == start)
  69                return 0;
  70        vma = find_vma(current->mm, start);
  71        if (!vma || vma->vm_start > start)
  72                return -ENOMEM;
  73
  74        for (nstart = start ; ; ) {
  75                unsigned int newflags;
  76
  77                /* Here we know that  vma->vm_start <= nstart < vma->vm_end. */
  78
  79                newflags = vma->vm_flags | VM_LOCKED;
  80                if (!on)
  81                        newflags &= ~VM_LOCKED;
  82
  83                if (vma->vm_end >= end) {
  84                        error = mlock_fixup(vma, nstart, end, newflags);
  85                        break;
  86                }
  87
  88                tmp = vma->vm_end;
  89                next = vma->vm_next;
  90                error = mlock_fixup(vma, nstart, tmp, newflags);
  91                if (error)
  92                        break;
  93                nstart = tmp;
  94                vma = next;
  95                if (!vma || vma->vm_start != nstart) {
  96                        error = -ENOMEM;
  97                        break;
  98                }
  99        }
 100        return error;
 101}
 102
 103asmlinkage long sys_mlock(unsigned long start, size_t len)
 104{
 105        unsigned long locked;
 106        unsigned long lock_limit;
 107        int error = -ENOMEM;
 108
 109        if (!can_do_mlock())
 110                return -EPERM;
 111
 112        down_write(&current->mm->mmap_sem);
 113        len = PAGE_ALIGN(len + (start & ~PAGE_MASK));
 114        start &= PAGE_MASK;
 115
 116        locked = len >> PAGE_SHIFT;
 117        locked += current->mm->locked_vm;
 118
 119        lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur;
 120        lock_limit >>= PAGE_SHIFT;
 121
 122        /* check against resource limits */
 123        if ((locked <= lock_limit) || capable(CAP_IPC_LOCK))
 124                error = do_mlock(start, len, 1);
 125        up_write(&current->mm->mmap_sem);
 126        return error;
 127}
 128
 129asmlinkage long sys_munlock(unsigned long start, size_t len)
 130{
 131        int ret;
 132
 133        down_write(&current->mm->mmap_sem);
 134        len = PAGE_ALIGN(len + (start & ~PAGE_MASK));
 135        start &= PAGE_MASK;
 136        ret = do_mlock(start, len, 0);
 137        up_write(&current->mm->mmap_sem);
 138        return ret;
 139}
 140
 141static int do_mlockall(int flags)
 142{
 143        struct vm_area_struct * vma;
 144        unsigned int def_flags = 0;
 145
 146        if (flags & MCL_FUTURE)
 147                def_flags = VM_LOCKED;
 148        current->mm->def_flags = def_flags;
 149        if (flags == MCL_FUTURE)
 150                goto out;
 151
 152        for (vma = current->mm->mmap; vma ; vma = vma->vm_next) {
 153                unsigned int newflags;
 154
 155                newflags = vma->vm_flags | VM_LOCKED;
 156                if (!(flags & MCL_CURRENT))
 157                        newflags &= ~VM_LOCKED;
 158
 159                /* Ignore errors */
 160                mlock_fixup(vma, vma->vm_start, vma->vm_end, newflags);
 161        }
 162out:
 163        return 0;
 164}
 165
 166asmlinkage long sys_mlockall(int flags)
 167{
 168        unsigned long lock_limit;
 169        int ret = -EINVAL;
 170
 171        if (!flags || (flags & ~(MCL_CURRENT | MCL_FUTURE)))
 172                goto out;
 173
 174        ret = -EPERM;
 175        if (!can_do_mlock())
 176                goto out;
 177
 178        down_write(&current->mm->mmap_sem);
 179
 180        lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur;
 181        lock_limit >>= PAGE_SHIFT;
 182
 183        ret = -ENOMEM;
 184        if (!(flags & MCL_CURRENT) || (current->mm->total_vm <= lock_limit) ||
 185            capable(CAP_IPC_LOCK))
 186                ret = do_mlockall(flags);
 187        up_write(&current->mm->mmap_sem);
 188out:
 189        return ret;
 190}
 191
 192asmlinkage long sys_munlockall(void)
 193{
 194        int ret;
 195
 196        down_write(&current->mm->mmap_sem);
 197        ret = do_mlockall(0);
 198        up_write(&current->mm->mmap_sem);
 199        return ret;
 200}
 201
 202/*
 203 * Objects with different lifetime than processes (SHM_LOCK and SHM_HUGETLB
 204 * shm segments) get accounted against the user_struct instead.
 205 */
 206static spinlock_t shmlock_user_lock = SPIN_LOCK_UNLOCKED;
 207
 208int user_shm_lock(size_t size, struct user_struct *user)
 209{
 210        unsigned long lock_limit, locked;
 211        int allowed = 0;
 212
 213        spin_lock(&shmlock_user_lock);
 214        locked = size >> PAGE_SHIFT;
 215        lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur;
 216        if (lock_limit == RLIM_INFINITY)
 217                allowed = 1;
 218        lock_limit >>= PAGE_SHIFT;
 219        if (!allowed &&
 220             locked + user->locked_shm > lock_limit && !capable(CAP_IPC_LOCK))
 221                goto out;
 222        get_uid(user);
 223        user->locked_shm += locked;
 224        allowed = 1;
 225out:
 226        spin_unlock(&shmlock_user_lock);
 227        return allowed;
 228}
 229
 230void user_shm_unlock(size_t size, struct user_struct *user)
 231{
 232        spin_lock(&shmlock_user_lock);
 233        user->locked_shm -= (size >> PAGE_SHIFT);
 234        spin_unlock(&shmlock_user_lock);
 235        free_uid(user);
 236}
 237