aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/hugetlbfs/inode.c12
-rw-r--r--include/asm-alpha/resource.h2
-rw-r--r--include/asm-arm/resource.h2
-rw-r--r--include/asm-arm26/resource.h2
-rw-r--r--include/asm-cris/resource.h2
-rw-r--r--include/asm-h8300/resource.h2
-rw-r--r--include/asm-i386/resource.h2
-rw-r--r--include/asm-ia64/resource.h2
-rw-r--r--include/asm-m68k/resource.h2
-rw-r--r--include/asm-parisc/resource.h2
-rw-r--r--include/asm-ppc/resource.h2
-rw-r--r--include/asm-ppc64/resource.h2
-rw-r--r--include/asm-s390/resource.h2
-rw-r--r--include/asm-sh/resource.h2
-rw-r--r--include/asm-sparc/resource.h2
-rw-r--r--include/asm-sparc64/resource.h2
-rw-r--r--include/asm-v850/resource.h2
-rw-r--r--include/asm-x86_64/resource.h2
-rw-r--r--include/linux/mm.h13
-rw-r--r--include/linux/sched.h1
-rw-r--r--include/linux/shm.h1
-rw-r--r--ipc/shm.c36
-rw-r--r--kernel/user.c4
-rw-r--r--mm/mlock.c41
-rw-r--r--mm/mmap.c14
-rw-r--r--mm/mremap.c6
-rw-r--r--mm/shmem.c15
27 files changed, 127 insertions, 50 deletions
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 90c07ffb7800a..d5aa417d89560 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -720,12 +720,13 @@ static unsigned long hugetlbfs_counter(void)
static int can_do_hugetlb_shm(void)
{
return likely(capable(CAP_IPC_LOCK) ||
- in_group_p(sysctl_hugetlb_shm_group));
+ in_group_p(sysctl_hugetlb_shm_group) ||
+ can_do_mlock());
}
struct file *hugetlb_zero_setup(size_t size)
{
- int error;
+ int error = -ENOMEM;
struct file *file;
struct inode *inode;
struct dentry *dentry, *root;
@@ -738,6 +739,9 @@ struct file *hugetlb_zero_setup(size_t size)
if (!is_hugepage_mem_enough(size))
return ERR_PTR(-ENOMEM);
+ if (!user_shm_lock(size, current->user))
+ return ERR_PTR(-ENOMEM);
+
root = hugetlbfs_vfsmount->mnt_root;
snprintf(buf, 16, "%lu", hugetlbfs_counter());
quick_string.name = buf;
@@ -745,7 +749,7 @@ struct file *hugetlb_zero_setup(size_t size)
quick_string.hash = 0;
dentry = d_alloc(root, &quick_string);
if (!dentry)
- return ERR_PTR(-ENOMEM);
+ goto out_shm_unlock;
error = -ENFILE;
file = get_empty_filp();
@@ -772,6 +776,8 @@ out_file:
put_filp(file);
out_dentry:
dput(dentry);
+out_shm_unlock:
+ user_shm_unlock(size, current->user);
return ERR_PTR(error);
}
diff --git a/include/asm-alpha/resource.h b/include/asm-alpha/resource.h
index b94759c615213..2b0f4bcf2644c 100644
--- a/include/asm-alpha/resource.h
+++ b/include/asm-alpha/resource.h
@@ -41,7 +41,7 @@
{INR_OPEN, INR_OPEN}, /* RLIMIT_NOFILE */ \
{LONG_MAX, LONG_MAX}, /* RLIMIT_AS */ \
{LONG_MAX, LONG_MAX}, /* RLIMIT_NPROC */ \
- {LONG_MAX, LONG_MAX}, /* RLIMIT_MEMLOCK */ \
+ {0, 0 }, /* RLIMIT_MEMLOCK */ \
{LONG_MAX, LONG_MAX}, /* RLIMIT_LOCKS */ \
{MAX_SIGPENDING, MAX_SIGPENDING}, /* RLIMIT_SIGPENDING */ \
{MQ_BYTES_MAX, MQ_BYTES_MAX}, /* RLIMIT_MSGQUEUE */ \
diff --git a/include/asm-arm/resource.h b/include/asm-arm/resource.h
index 748c660edb154..323167464b976 100644
--- a/include/asm-arm/resource.h
+++ b/include/asm-arm/resource.h
@@ -39,7 +39,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING}, \
diff --git a/include/asm-arm26/resource.h b/include/asm-arm26/resource.h
index 748c660edb154..28a05990277d8 100644
--- a/include/asm-arm26/resource.h
+++ b/include/asm-arm26/resource.h
@@ -39,7 +39,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING}, \
diff --git a/include/asm-cris/resource.h b/include/asm-cris/resource.h
index e33ada08d9b82..606a4c9a95798 100644
--- a/include/asm-cris/resource.h
+++ b/include/asm-cris/resource.h
@@ -39,7 +39,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING }, \
diff --git a/include/asm-h8300/resource.h b/include/asm-h8300/resource.h
index a87720b14a900..65cf2c6962f26 100644
--- a/include/asm-h8300/resource.h
+++ b/include/asm-h8300/resource.h
@@ -39,7 +39,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING }, \
diff --git a/include/asm-i386/resource.h b/include/asm-i386/resource.h
index 3e391b2e941f3..47bdff24d040d 100644
--- a/include/asm-i386/resource.h
+++ b/include/asm-i386/resource.h
@@ -40,7 +40,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING }, \
diff --git a/include/asm-ia64/resource.h b/include/asm-ia64/resource.h
index 76345b5c14c8f..c0a403a8a42e0 100644
--- a/include/asm-ia64/resource.h
+++ b/include/asm-ia64/resource.h
@@ -46,7 +46,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING }, \
diff --git a/include/asm-m68k/resource.h b/include/asm-m68k/resource.h
index 8362001c286cd..51ef4bbb8e6ad 100644
--- a/include/asm-m68k/resource.h
+++ b/include/asm-m68k/resource.h
@@ -39,7 +39,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING }, \
diff --git a/include/asm-parisc/resource.h b/include/asm-parisc/resource.h
index 59a446534c5b6..ac9de533eb622 100644
--- a/include/asm-parisc/resource.h
+++ b/include/asm-parisc/resource.h
@@ -39,7 +39,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING }, \
diff --git a/include/asm-ppc/resource.h b/include/asm-ppc/resource.h
index 3d29914559fc5..a8392167ae6e6 100644
--- a/include/asm-ppc/resource.h
+++ b/include/asm-ppc/resource.h
@@ -36,7 +36,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING }, \
diff --git a/include/asm-ppc64/resource.h b/include/asm-ppc64/resource.h
index c54e9d69d829b..d23ea5ba1b9b0 100644
--- a/include/asm-ppc64/resource.h
+++ b/include/asm-ppc64/resource.h
@@ -45,7 +45,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING }, \
diff --git a/include/asm-s390/resource.h b/include/asm-s390/resource.h
index 5f0f2ba958dd7..837ed3ab12756 100644
--- a/include/asm-s390/resource.h
+++ b/include/asm-s390/resource.h
@@ -47,7 +47,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING }, \
diff --git a/include/asm-sh/resource.h b/include/asm-sh/resource.h
index 73e517a3e80f3..690f83a92b210 100644
--- a/include/asm-sh/resource.h
+++ b/include/asm-sh/resource.h
@@ -39,7 +39,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING }, \
diff --git a/include/asm-sparc/resource.h b/include/asm-sparc/resource.h
index 58e90f72ca708..098bfa7145f80 100644
--- a/include/asm-sparc/resource.h
+++ b/include/asm-sparc/resource.h
@@ -44,7 +44,7 @@
{ 0, RLIM_INFINITY}, \
{RLIM_INFINITY, RLIM_INFINITY}, \
{INR_OPEN, INR_OPEN}, {0, 0}, \
- {RLIM_INFINITY, RLIM_INFINITY}, \
+ {0, 0}, \
{RLIM_INFINITY, RLIM_INFINITY}, \
{RLIM_INFINITY, RLIM_INFINITY}, \
{MAX_SIGPENDING, MAX_SIGPENDING}, \
diff --git a/include/asm-sparc64/resource.h b/include/asm-sparc64/resource.h
index 4a77dd6206217..60afa3362b7fd 100644
--- a/include/asm-sparc64/resource.h
+++ b/include/asm-sparc64/resource.h
@@ -43,7 +43,7 @@
{ 0, RLIM_INFINITY}, \
{RLIM_INFINITY, RLIM_INFINITY}, \
{INR_OPEN, INR_OPEN}, {0, 0}, \
- {RLIM_INFINITY, RLIM_INFINITY}, \
+ {0, 0 }, \
{RLIM_INFINITY, RLIM_INFINITY}, \
{RLIM_INFINITY, RLIM_INFINITY}, \
{MAX_SIGPENDING, MAX_SIGPENDING}, \
diff --git a/include/asm-v850/resource.h b/include/asm-v850/resource.h
index 9f4ca4ae638f8..0b757f33dd921 100644
--- a/include/asm-v850/resource.h
+++ b/include/asm-v850/resource.h
@@ -39,7 +39,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING }, \
diff --git a/include/asm-x86_64/resource.h b/include/asm-x86_64/resource.h
index 9628f77179fd1..4ed168acafb8e 100644
--- a/include/asm-x86_64/resource.h
+++ b/include/asm-x86_64/resource.h
@@ -39,7 +39,7 @@
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ INR_OPEN, INR_OPEN }, \
- { RLIM_INFINITY, RLIM_INFINITY }, \
+ { 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ MAX_SIGPENDING, MAX_SIGPENDING }, \
diff --git a/include/linux/mm.h b/include/linux/mm.h
index fc87162452946..b7859da6d3339 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -496,9 +496,20 @@ int shmem_set_policy(struct vm_area_struct *vma, struct mempolicy *new);
struct mempolicy *shmem_get_policy(struct vm_area_struct *vma,
unsigned long addr);
struct file *shmem_file_setup(char * name, loff_t size, unsigned long flags);
-void shmem_lock(struct file * file, int lock);
+int shmem_lock(struct file *file, int lock, struct user_struct *user);
int shmem_zero_setup(struct vm_area_struct *);
+static inline int can_do_mlock(void)
+{
+ if (capable(CAP_IPC_LOCK))
+ return 1;
+ if (current->rlim[RLIMIT_MEMLOCK].rlim_cur != 0)
+ return 1;
+ return 0;
+}
+extern int user_shm_lock(size_t, struct user_struct *);
+extern void user_shm_unlock(size_t, struct user_struct *);
+
/*
* Parameter block passed down to zap_pte_range in exceptional cases.
*/
diff --git a/include/linux/sched.h b/include/linux/sched.h
index a01f849da7a5a..24066551b9668 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -316,6 +316,7 @@ struct user_struct {
atomic_t sigpending; /* How many pending signals does this user have? */
/* protected by mq_lock */
unsigned long mq_bytes; /* How many bytes can be allocated to mqueue? */
+ unsigned long locked_shm; /* How many pages of mlocked shm ? */
/* Hash table maintenance information */
struct list_head uidhash_list;
diff --git a/include/linux/shm.h b/include/linux/shm.h
index 9a00f5ff6c581..1907355c0eb14 100644
--- a/include/linux/shm.h
+++ b/include/linux/shm.h
@@ -84,6 +84,7 @@ struct shmid_kernel /* private to the kernel */
time_t shm_ctim;
pid_t shm_cprid;
pid_t shm_lprid;
+ struct user_struct *mlock_user;
};
/* shm_mode upper byte flags */
diff --git a/ipc/shm.c b/ipc/shm.c
index de76c1961367a..55dc1ba4229e1 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -114,7 +114,10 @@ static void shm_destroy (struct shmid_kernel *shp)
shm_rmid (shp->id);
shm_unlock(shp);
if (!is_file_hugepages(shp->shm_file))
- shmem_lock(shp->shm_file, 0);
+ shmem_lock(shp->shm_file, 0, shp->mlock_user);
+ else
+ user_shm_unlock(shp->shm_file->f_dentry->d_inode->i_size,
+ shp->mlock_user);
fput (shp->shm_file);
security_shm_free(shp);
ipc_rcu_putref(shp);
@@ -190,6 +193,7 @@ static int newseg (key_t key, int shmflg, size_t size)
shp->shm_perm.key = key;
shp->shm_flags = (shmflg & S_IRWXUGO);
+ shp->mlock_user = NULL;
shp->shm_perm.security = NULL;
error = security_shm_alloc(shp);
@@ -198,9 +202,11 @@ static int newseg (key_t key, int shmflg, size_t size)
return error;
}
- if (shmflg & SHM_HUGETLB)
+ if (shmflg & SHM_HUGETLB) {
+ /* hugetlb_zero_setup takes care of mlock user accounting */
file = hugetlb_zero_setup(size);
- else {
+ shp->mlock_user = current->user;
+ } else {
sprintf (name, "SYSV%08x", key);
file = shmem_file_setup(name, size, VM_ACCOUNT);
}
@@ -504,14 +510,11 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
case SHM_LOCK:
case SHM_UNLOCK:
{
-/* Allow superuser to lock segment in memory */
-/* Should the pages be faulted in here or leave it to user? */
-/* need to determine interaction with current->swappable */
- if (!capable(CAP_IPC_LOCK)) {
+ /* Allow superuser to lock segment in memory */
+ if (!can_do_mlock() && cmd == SHM_LOCK) {
err = -EPERM;
goto out;
}
-
shp = shm_lock(shmid);
if(shp==NULL) {
err = -EINVAL;
@@ -526,13 +529,18 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
goto out_unlock;
if(cmd==SHM_LOCK) {
- if (!is_file_hugepages(shp->shm_file))
- shmem_lock(shp->shm_file, 1);
- shp->shm_flags |= SHM_LOCKED;
- } else {
- if (!is_file_hugepages(shp->shm_file))
- shmem_lock(shp->shm_file, 0);
+ struct user_struct * user = current->user;
+ if (!is_file_hugepages(shp->shm_file)) {
+ err = shmem_lock(shp->shm_file, 1, user);
+ if (!err) {
+ shp->shm_flags |= SHM_LOCKED;
+ shp->mlock_user = user;
+ }
+ }
+ } else if (!is_file_hugepages(shp->shm_file)) {
+ shmem_lock(shp->shm_file, 0, shp->mlock_user);
shp->shm_flags &= ~SHM_LOCKED;
+ shp->mlock_user = NULL;
}
shm_unlock(shp);
goto out;
diff --git a/kernel/user.c b/kernel/user.c
index 9f9859ef88ea2..523175afeecdc 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -32,7 +32,8 @@ struct user_struct root_user = {
.processes = ATOMIC_INIT(1),
.files = ATOMIC_INIT(0),
.sigpending = ATOMIC_INIT(0),
- .mq_bytes = 0
+ .mq_bytes = 0,
+ .locked_shm = 0,
};
/*
@@ -113,6 +114,7 @@ struct user_struct * alloc_uid(uid_t uid)
atomic_set(&new->sigpending, 0);
new->mq_bytes = 0;
+ new->locked_shm = 0;
/*
* Before adding this, check whether we raced
diff --git a/mm/mlock.c b/mm/mlock.c
index a9e37161dcef6..b428752c61870 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -60,7 +60,7 @@ static int do_mlock(unsigned long start, size_t len, int on)
struct vm_area_struct * vma, * next;
int error;
- if (on && !capable(CAP_IPC_LOCK))
+ if (on && !can_do_mlock())
return -EPERM;
len = PAGE_ALIGN(len);
end = start + len;
@@ -118,7 +118,7 @@ asmlinkage long sys_mlock(unsigned long start, size_t len)
lock_limit >>= PAGE_SHIFT;
/* check against resource limits */
- if (locked <= lock_limit)
+ if ( (locked <= lock_limit) || capable(CAP_IPC_LOCK))
error = do_mlock(start, len, 1);
up_write(&current->mm->mmap_sem);
return error;
@@ -142,7 +142,7 @@ static int do_mlockall(int flags)
unsigned int def_flags;
struct vm_area_struct * vma;
- if (!capable(CAP_IPC_LOCK))
+ if (!can_do_mlock())
return -EPERM;
def_flags = 0;
@@ -177,7 +177,7 @@ asmlinkage long sys_mlockall(int flags)
lock_limit >>= PAGE_SHIFT;
ret = -ENOMEM;
- if (current->mm->total_vm <= lock_limit)
+ if ((current->mm->total_vm <= lock_limit) || capable(CAP_IPC_LOCK))
ret = do_mlockall(flags);
out:
up_write(&current->mm->mmap_sem);
@@ -193,3 +193,36 @@ asmlinkage long sys_munlockall(void)
up_write(&current->mm->mmap_sem);
return ret;
}
+
+/*
+ * Objects with different lifetime than processes (SHM_LOCK and SHM_HUGETLB
+ * shm segments) get accounted against the user_struct instead.
+ */
+static spinlock_t shmlock_user_lock = SPIN_LOCK_UNLOCKED;
+
+int user_shm_lock(size_t size, struct user_struct *user)
+{
+ unsigned long lock_limit, locked;
+ int allowed = 0;
+
+ spin_lock(&shmlock_user_lock);
+ locked = size >> PAGE_SHIFT;
+ lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur;
+ lock_limit >>= PAGE_SHIFT;
+ if (locked + user->locked_shm > lock_limit && !capable(CAP_IPC_LOCK))
+ goto out;
+ get_uid(user);
+ user->locked_shm += locked;
+ allowed = 1;
+out:
+ spin_unlock(&shmlock_user_lock);
+ return allowed;
+}
+
+void user_shm_unlock(size_t size, struct user_struct *user)
+{
+ spin_lock(&shmlock_user_lock);
+ user->locked_shm -= (size >> PAGE_SHIFT);
+ spin_unlock(&shmlock_user_lock);
+ free_uid(user);
+}
diff --git a/mm/mmap.c b/mm/mmap.c
index 3f7495c75228c..04dc9e2849183 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -796,15 +796,17 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
if (flags & MAP_LOCKED) {
- if (!capable(CAP_IPC_LOCK))
+ if (!can_do_mlock())
return -EPERM;
vm_flags |= VM_LOCKED;
}
/* mlock MCL_FUTURE? */
if (vm_flags & VM_LOCKED) {
- unsigned long locked = mm->locked_vm << PAGE_SHIFT;
+ unsigned long locked, lock_limit;
+ locked = mm->locked_vm << PAGE_SHIFT;
+ lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur;
locked += len;
- if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)
+ if (locked > lock_limit && !capable(CAP_IPC_LOCK))
return -EAGAIN;
}
@@ -1625,9 +1627,11 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
* mlock MCL_FUTURE?
*/
if (mm->def_flags & VM_LOCKED) {
- unsigned long locked = mm->locked_vm << PAGE_SHIFT;
+ unsigned long locked, lock_limit;
+ locked = mm->locked_vm << PAGE_SHIFT;
+ lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur;
locked += len;
- if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)
+ if (locked > lock_limit && !capable(CAP_IPC_LOCK))
return -EAGAIN;
}
diff --git a/mm/mremap.c b/mm/mremap.c
index 984b8ddbd2184..6be63314688f1 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -324,10 +324,12 @@ unsigned long do_mremap(unsigned long addr,
goto out;
}
if (vma->vm_flags & VM_LOCKED) {
- unsigned long locked = current->mm->locked_vm << PAGE_SHIFT;
+ unsigned long locked, lock_limit;
+ locked = current->mm->locked_vm << PAGE_SHIFT;
+ lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur;
locked += new_len - old_len;
ret = -EAGAIN;
- if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)
+ if (locked > lock_limit && !capable(CAP_IPC_LOCK))
goto out;
}
ret = -ENOMEM;
diff --git a/mm/shmem.c b/mm/shmem.c
index 8ca20e04b60cb..c3b4cc5d59ff0 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1151,17 +1151,26 @@ shmem_get_policy(struct vm_area_struct *vma, unsigned long addr)
}
#endif
-void shmem_lock(struct file *file, int lock)
+int shmem_lock(struct file *file, int lock, struct user_struct *user)
{
struct inode *inode = file->f_dentry->d_inode;
struct shmem_inode_info *info = SHMEM_I(inode);
+ int retval = -ENOMEM;
spin_lock(&info->lock);
- if (lock)
+ if (lock && !(info->flags & VM_LOCKED)) {
+ if (!user_shm_lock(inode->i_size, user))
+ goto out_nomem;
info->flags |= VM_LOCKED;
- else
+ }
+ if (!lock && (info->flags & VM_LOCKED) && user) {
+ user_shm_unlock(inode->i_size, user);
info->flags &= ~VM_LOCKED;
+ }
+ retval = 0;
+out_nomem:
spin_unlock(&info->lock);
+ return retval;
}
static int shmem_mmap(struct file *file, struct vm_area_struct *vma)