MIPS/ptrace: Update mips_get_syscall_arg's return type
[linux-2.6-microblaze.git] / mm / util.c
index 68575a3..e6351a8 100644 (file)
--- a/mm/util.c
+++ b/mm/util.c
@@ -7,6 +7,7 @@
 #include <linux/err.h>
 #include <linux/sched.h>
 #include <linux/sched/mm.h>
+#include <linux/sched/signal.h>
 #include <linux/sched/task_stack.h>
 #include <linux/security.h>
 #include <linux/swap.h>
@@ -300,6 +301,80 @@ void arch_pick_mmap_layout(struct mm_struct *mm, struct rlimit *rlim_stack)
 }
 #endif
 
+/**
+ * __account_locked_vm - account locked pages to an mm's locked_vm
+ * @mm:          mm to account against
+ * @pages:       number of pages to account
+ * @inc:         %true if @pages should be considered positive, %false if not
+ * @task:        task used to check RLIMIT_MEMLOCK
+ * @bypass_rlim: %true if checking RLIMIT_MEMLOCK should be skipped
+ *
+ * Assumes @task and @mm are valid (i.e. at least one reference on each), and
+ * that mmap_sem is held as writer.
+ *
+ * Return:
+ * * 0       on success
+ * * -ENOMEM if RLIMIT_MEMLOCK would be exceeded.
+ */
+int __account_locked_vm(struct mm_struct *mm, unsigned long pages, bool inc,
+                       struct task_struct *task, bool bypass_rlim)
+{
+       unsigned long locked_vm, limit;
+       int ret = 0;
+
+       lockdep_assert_held_write(&mm->mmap_sem);
+
+       locked_vm = mm->locked_vm;
+       if (inc) {
+               if (!bypass_rlim) {
+                       limit = task_rlimit(task, RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+                       if (locked_vm + pages > limit)
+                               ret = -ENOMEM;
+               }
+               if (!ret)
+                       mm->locked_vm = locked_vm + pages;
+       } else {
+               WARN_ON_ONCE(pages > locked_vm);
+               mm->locked_vm = locked_vm - pages;
+       }
+
+       pr_debug("%s: [%d] caller %ps %c%lu %lu/%lu%s\n", __func__, task->pid,
+                (void *)_RET_IP_, (inc) ? '+' : '-', pages << PAGE_SHIFT,
+                locked_vm << PAGE_SHIFT, task_rlimit(task, RLIMIT_MEMLOCK),
+                ret ? " - exceeded" : "");
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(__account_locked_vm);
+
+/**
+ * account_locked_vm - account locked pages to an mm's locked_vm
+ * @mm:          mm to account against, may be NULL
+ * @pages:       number of pages to account
+ * @inc:         %true if @pages should be considered positive, %false if not
+ *
+ * Assumes a non-NULL @mm is valid (i.e. at least one reference on it).
+ *
+ * Return:
+ * * 0       on success, or if mm is NULL
+ * * -ENOMEM if RLIMIT_MEMLOCK would be exceeded.
+ */
+int account_locked_vm(struct mm_struct *mm, unsigned long pages, bool inc)
+{
+       int ret;
+
+       if (pages == 0 || !mm)
+               return 0;
+
+       down_write(&mm->mmap_sem);
+       ret = __account_locked_vm(mm, pages, inc, current,
+                                 capable(CAP_IPC_LOCK));
+       up_write(&mm->mmap_sem);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(account_locked_vm);
+
 unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr,
        unsigned long len, unsigned long prot,
        unsigned long flag, unsigned long pgoff)