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