linux-old/mm/mlock.c
<<
>>
Prefs
   1/*
   2 *      linux/mm/mlock.c
   3 *
   4 *  (C) Copyright 1995 Linus Torvalds
   5 */
   6#include <linux/stat.h>
   7#include <linux/sched.h>
   8#include <linux/kernel.h>
   9#include <linux/mm.h>
  10#include <linux/slab.h>
  11#include <linux/shm.h>
  12#include <linux/errno.h>
  13#include <linux/mman.h>
  14#include <linux/string.h>
  15#include <linux/smp.h>
  16#include <linux/smp_lock.h>
  17
  18#include <asm/uaccess.h>
  19#include <asm/system.h>
  20#include <asm/pgtable.h>
  21
  22static inline int mlock_fixup_all(struct vm_area_struct * vma, int newflags)
  23{
  24        vma->vm_flags = newflags;
  25        return 0;
  26}
  27
  28static inline int mlock_fixup_start(struct vm_area_struct * vma,
  29        unsigned long end, int newflags)
  30{
  31        struct vm_area_struct * n;
  32
  33        n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
  34        if (!n)
  35                return -EAGAIN;
  36        *n = *vma;
  37        vma->vm_start = end;
  38        n->vm_end = end;
  39        vma->vm_offset += vma->vm_start - n->vm_start;
  40        n->vm_flags = newflags;
  41        n->vm_dentry = dget(vma->vm_dentry);
  42        if (n->vm_ops && n->vm_ops->open)
  43                n->vm_ops->open(n);
  44        insert_vm_struct(current->mm, n);
  45        return 0;
  46}
  47
  48static inline int mlock_fixup_end(struct vm_area_struct * vma,
  49        unsigned long start, int newflags)
  50{
  51        struct vm_area_struct * n;
  52
  53        n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
  54        if (!n)
  55                return -EAGAIN;
  56        *n = *vma;
  57        vma->vm_end = start;
  58        n->vm_start = start;
  59        n->vm_offset += n->vm_start - vma->vm_start;
  60        n->vm_flags = newflags;
  61        n->vm_dentry = dget(vma->vm_dentry);
  62        if (n->vm_ops && n->vm_ops->open)
  63                n->vm_ops->open(n);
  64        insert_vm_struct(current->mm, n);
  65        return 0;
  66}
  67
  68static inline int mlock_fixup_middle(struct vm_area_struct * vma,
  69        unsigned long start, unsigned long end, int newflags)
  70{
  71        struct vm_area_struct * left, * right;
  72
  73        left = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
  74        if (!left)
  75                return -EAGAIN;
  76        right = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
  77        if (!right) {
  78                kmem_cache_free(vm_area_cachep, left);
  79                return -EAGAIN;
  80        }
  81        *left = *vma;
  82        *right = *vma;
  83        left->vm_end = start;
  84        vma->vm_start = start;
  85        vma->vm_end = end;
  86        right->vm_start = end;
  87        vma->vm_offset += vma->vm_start - left->vm_start;
  88        right->vm_offset += right->vm_start - left->vm_start;
  89        vma->vm_flags = newflags;
  90        if (vma->vm_dentry)
  91                vma->vm_dentry->d_count += 2;
  92
  93        if (vma->vm_ops && vma->vm_ops->open) {
  94                vma->vm_ops->open(left);
  95                vma->vm_ops->open(right);
  96        }
  97        insert_vm_struct(current->mm, left);
  98        insert_vm_struct(current->mm, right);
  99        return 0;
 100}
 101
 102static int mlock_fixup(struct vm_area_struct * vma, 
 103        unsigned long start, unsigned long end, unsigned int newflags)
 104{
 105        int pages, retval;
 106
 107        if (newflags == vma->vm_flags)
 108                return 0;
 109
 110        if (start == vma->vm_start) {
 111                if (end == vma->vm_end)
 112                        retval = mlock_fixup_all(vma, newflags);
 113                else
 114                        retval = mlock_fixup_start(vma, end, newflags);
 115        } else {
 116                if (end == vma->vm_end)
 117                        retval = mlock_fixup_end(vma, start, newflags);
 118                else
 119                        retval = mlock_fixup_middle(vma, start, end, newflags);
 120        }
 121        if (!retval) {
 122                /* keep track of amount of locked VM */
 123                pages = (end - start) >> PAGE_SHIFT;
 124                if (!(newflags & VM_LOCKED))
 125                        pages = -pages;
 126                vma->vm_mm->locked_vm += pages;
 127
 128                if (newflags & VM_LOCKED)
 129                        while (start < end) {
 130                                char c;
 131                                get_user(c,(char *) start);
 132                                __asm__ __volatile__("": :"r" (c));
 133                                start += PAGE_SIZE;
 134                        }
 135        }
 136        return retval;
 137}
 138
 139static int do_mlock(unsigned long start, size_t len, int on)
 140{
 141        unsigned long nstart, end, tmp;
 142        struct vm_area_struct * vma, * next;
 143        int error;
 144
 145        if (!suser())
 146                return -EPERM;
 147        len = (len + ~PAGE_MASK) & PAGE_MASK;
 148        end = start + len;
 149        if (end < start)
 150                return -EINVAL;
 151        if (end == start)
 152                return 0;
 153        vma = find_vma(current->mm, start);
 154        if (!vma || vma->vm_start > start)
 155                return -ENOMEM;
 156
 157        for (nstart = start ; ; ) {
 158                unsigned int newflags;
 159
 160                /* Here we know that  vma->vm_start <= nstart < vma->vm_end. */
 161
 162                newflags = vma->vm_flags | VM_LOCKED;
 163                if (!on)
 164                        newflags &= ~VM_LOCKED;
 165
 166                if (vma->vm_end >= end) {
 167                        error = mlock_fixup(vma, nstart, end, newflags);
 168                        break;
 169                }
 170
 171                tmp = vma->vm_end;
 172                next = vma->vm_next;
 173                error = mlock_fixup(vma, nstart, tmp, newflags);
 174                if (error)
 175                        break;
 176                nstart = tmp;
 177                vma = next;
 178                if (!vma || vma->vm_start != nstart) {
 179                        error = -ENOMEM;
 180                        break;
 181                }
 182        }
 183        merge_segments(current->mm, start, end);
 184        return error;
 185}
 186
 187asmlinkage int sys_mlock(unsigned long start, size_t len)
 188{
 189        unsigned long locked;
 190        unsigned long lock_limit;
 191        int error = -ENOMEM;
 192
 193        lock_kernel();
 194        len = (len + (start & ~PAGE_MASK) + ~PAGE_MASK) & PAGE_MASK;
 195        start &= PAGE_MASK;
 196
 197        locked = len >> PAGE_SHIFT;
 198        locked += current->mm->locked_vm;
 199
 200        lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur;
 201        lock_limit >>= PAGE_SHIFT;
 202
 203        /* check against resource limits */
 204        if (locked > lock_limit)
 205                goto out;
 206
 207        /* we may lock at most half of physical memory... */
 208        /* (this check is pretty bogus, but doesn't hurt) */
 209        if (locked > num_physpages/2)
 210                goto out;
 211
 212        error = do_mlock(start, len, 1);
 213out:
 214        unlock_kernel();
 215        return error;
 216}
 217
 218asmlinkage int sys_munlock(unsigned long start, size_t len)
 219{
 220        int ret;
 221
 222        lock_kernel();
 223        len = (len + (start & ~PAGE_MASK) + ~PAGE_MASK) & PAGE_MASK;
 224        start &= PAGE_MASK;
 225        ret = do_mlock(start, len, 0);
 226        unlock_kernel();
 227        return ret;
 228}
 229
 230static int do_mlockall(int flags)
 231{
 232        int error;
 233        unsigned int def_flags;
 234        struct vm_area_struct * vma;
 235
 236        if (!suser())
 237                return -EPERM;
 238
 239        def_flags = 0;
 240        if (flags & MCL_FUTURE)
 241                def_flags = VM_LOCKED;
 242        current->mm->def_flags = def_flags;
 243
 244        error = 0;
 245        for (vma = current->mm->mmap; vma ; vma = vma->vm_next) {
 246                unsigned int newflags;
 247
 248                newflags = vma->vm_flags | VM_LOCKED;
 249                if (!(flags & MCL_CURRENT))
 250                        newflags &= ~VM_LOCKED;
 251                error = mlock_fixup(vma, vma->vm_start, vma->vm_end, newflags);
 252                if (error)
 253                        break;
 254        }
 255        merge_segments(current->mm, 0, TASK_SIZE);
 256        return error;
 257}
 258
 259asmlinkage int sys_mlockall(int flags)
 260{
 261        unsigned long lock_limit;
 262        int ret = -EINVAL;
 263
 264        lock_kernel();
 265        if (!flags || (flags & ~(MCL_CURRENT | MCL_FUTURE)))
 266                goto out;
 267
 268        lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur;
 269        lock_limit >>= PAGE_SHIFT;
 270
 271        ret = -ENOMEM;
 272        if (current->mm->total_vm > lock_limit)
 273                goto out;
 274
 275        /* we may lock at most half of physical memory... */
 276        /* (this check is pretty bogus, but doesn't hurt) */
 277        if (current->mm->total_vm > num_physpages/2)
 278                goto out;
 279
 280        ret = do_mlockall(flags);
 281out:
 282        unlock_kernel();
 283        return ret;
 284}
 285
 286asmlinkage int sys_munlockall(void)
 287{
 288        int ret;
 289
 290        lock_kernel();
 291        ret = do_mlockall(0);
 292        unlock_kernel();
 293        return ret;
 294}
 295
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.