mm: limit growth of 3% hardcoded other user reserve
[cascardo/linux.git] / mm / mmap.c
index 081e6da..80a965f 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -84,6 +84,7 @@ EXPORT_SYMBOL(vm_get_page_prot);
 int sysctl_overcommit_memory __read_mostly = OVERCOMMIT_GUESS;  /* heuristic overcommit */
 int sysctl_overcommit_ratio __read_mostly = 50;        /* default is 50% */
 int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT;
+unsigned long sysctl_user_reserve_kbytes __read_mostly = 1UL << 17; /* 128MB */
 /*
  * Make sure vm_committed_as in one cacheline and not cacheline shared with
  * other variables. It can be updated by several CPUs frequently.
@@ -122,7 +123,7 @@ EXPORT_SYMBOL_GPL(vm_memory_committed);
  */
 int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin)
 {
-       unsigned long free, allowed;
+       unsigned long free, allowed, reserve;
 
        vm_acct_memory(pages);
 
@@ -183,10 +184,13 @@ int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin)
                allowed -= allowed / 32;
        allowed += total_swap_pages;
 
-       /* Don't let a single process grow too big:
-          leave 3% of the size of this process for other processes */
-       if (mm)
-               allowed -= mm->total_vm / 32;
+       /*
+        * Don't let a single process grow so big a user can't recover
+        */
+       if (mm) {
+               reserve = sysctl_user_reserve_kbytes >> (PAGE_SHIFT - 10);
+               allowed -= min(mm->total_vm / 32, reserve);
+       }
 
        if (percpu_counter_read_positive(&vm_committed_as) < allowed)
                return 0;
@@ -3094,3 +3098,24 @@ void __init mmap_init(void)
        ret = percpu_counter_init(&vm_committed_as, 0);
        VM_BUG_ON(ret);
 }
+
+/*
+ * Initialise sysctl_user_reserve_kbytes.
+ *
+ * This is intended to prevent a user from starting a single memory hogging
+ * process, such that they cannot recover (kill the hog) in OVERCOMMIT_NEVER
+ * mode.
+ *
+ * The default value is min(3% of free memory, 128MB)
+ * 128MB is enough to recover with sshd/login, bash, and top/kill.
+ */
+static int __meminit init_user_reserve(void)
+{
+       unsigned long free_kbytes;
+
+       free_kbytes = global_page_state(NR_FREE_PAGES) << (PAGE_SHIFT - 10);
+
+       sysctl_user_reserve_kbytes = min(free_kbytes / 32, 1UL << 17);
+       return 0;
+}
+module_init(init_user_reserve)