]> Pileus Git - ~andy/linux/blobdiff - mm/mmap.c
rtc: rtc-davinci: use module_platform_driver_probe()
[~andy/linux] / mm / mmap.c
index 80a965f352513b35bef2463051ab13476eff6a42..da3e9c04bf370fe5d591d72aa6222a8c47ec402c 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -6,6 +6,7 @@
  * Address space accounting code       <alan@lxorguk.ukuu.org.uk>
  */
 
+#include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/backing-dev.h>
 #include <linux/mm.h>
@@ -33,6 +34,8 @@
 #include <linux/uprobes.h>
 #include <linux/rbtree_augmented.h>
 #include <linux/sched/sysctl.h>
+#include <linux/notifier.h>
+#include <linux/memory.h>
 
 #include <asm/uaccess.h>
 #include <asm/cacheflush.h>
@@ -85,6 +88,7 @@ int sysctl_overcommit_memory __read_mostly = OVERCOMMIT_GUESS;  /* heuristic ove
 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 */
+unsigned long sysctl_admin_reserve_kbytes __read_mostly = 1UL << 13; /* 8MB */
 /*
  * Make sure vm_committed_as in one cacheline and not cacheline shared with
  * other variables. It can be updated by several CPUs frequently.
@@ -164,10 +168,10 @@ int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin)
                        free -= totalreserve_pages;
 
                /*
-                * Leave the last 3% for root
+                * Reserve some for root
                 */
                if (!cap_sys_admin)
-                       free -= free / 32;
+                       free -= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10);
 
                if (free > pages)
                        return 0;
@@ -178,10 +182,10 @@ int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin)
        allowed = (totalram_pages - hugetlb_total_pages())
                * sysctl_overcommit_ratio / 100;
        /*
-        * Leave the last 3% for root
+        * Reserve some for root
         */
        if (!cap_sys_admin)
-               allowed -= allowed / 32;
+               allowed -= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10);
        allowed += total_swap_pages;
 
        /*
@@ -547,6 +551,34 @@ static int find_vma_links(struct mm_struct *mm, unsigned long addr,
        return 0;
 }
 
+static unsigned long count_vma_pages_range(struct mm_struct *mm,
+               unsigned long addr, unsigned long end)
+{
+       unsigned long nr_pages = 0;
+       struct vm_area_struct *vma;
+
+       /* Find first overlaping mapping */
+       vma = find_vma_intersection(mm, addr, end);
+       if (!vma)
+               return 0;
+
+       nr_pages = (min(end, vma->vm_end) -
+               max(addr, vma->vm_start)) >> PAGE_SHIFT;
+
+       /* Iterate over the rest of the overlaps */
+       for (vma = vma->vm_next; vma; vma = vma->vm_next) {
+               unsigned long overlap_len;
+
+               if (vma->vm_start > end)
+                       break;
+
+               overlap_len = min(end, vma->vm_end) - vma->vm_start;
+               nr_pages += overlap_len >> PAGE_SHIFT;
+       }
+
+       return nr_pages;
+}
+
 void __vma_link_rb(struct mm_struct *mm, struct vm_area_struct *vma,
                struct rb_node **rb_link, struct rb_node *rb_parent)
 {
@@ -1439,6 +1471,23 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
        unsigned long charged = 0;
        struct inode *inode =  file ? file_inode(file) : NULL;
 
+       /* Check against address space limit. */
+       if (!may_expand_vm(mm, len >> PAGE_SHIFT)) {
+               unsigned long nr_pages;
+
+               /*
+                * MAP_FIXED may remove pages of mappings that intersects with
+                * requested mapping. Account for the pages it would unmap.
+                */
+               if (!(vm_flags & MAP_FIXED))
+                       return -ENOMEM;
+
+               nr_pages = count_vma_pages_range(mm, addr, addr + len);
+
+               if (!may_expand_vm(mm, (len >> PAGE_SHIFT) - nr_pages))
+                       return -ENOMEM;
+       }
+
        /* Clear old maps */
        error = -ENOMEM;
 munmap_back:
@@ -1448,10 +1497,6 @@ munmap_back:
                goto munmap_back;
        }
 
-       /* Check against address space limit. */
-       if (!may_expand_vm(mm, len >> PAGE_SHIFT))
-               return -ENOMEM;
-
        /*
         * Private writable mapping: check memory availability
         */
@@ -3109,7 +3154,7 @@ void __init mmap_init(void)
  * 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)
+static int init_user_reserve(void)
 {
        unsigned long free_kbytes;
 
@@ -3119,3 +3164,94 @@ static int __meminit init_user_reserve(void)
        return 0;
 }
 module_init(init_user_reserve)
+
+/*
+ * Initialise sysctl_admin_reserve_kbytes.
+ *
+ * The purpose of sysctl_admin_reserve_kbytes is to allow the sys admin
+ * to log in and kill a memory hogging process.
+ *
+ * Systems with more than 256MB will reserve 8MB, enough to recover
+ * with sshd, bash, and top in OVERCOMMIT_GUESS. Smaller systems will
+ * only reserve 3% of free pages by default.
+ */
+static int init_admin_reserve(void)
+{
+       unsigned long free_kbytes;
+
+       free_kbytes = global_page_state(NR_FREE_PAGES) << (PAGE_SHIFT - 10);
+
+       sysctl_admin_reserve_kbytes = min(free_kbytes / 32, 1UL << 13);
+       return 0;
+}
+module_init(init_admin_reserve)
+
+/*
+ * Reinititalise user and admin reserves if memory is added or removed.
+ *
+ * The default user reserve max is 128MB, and the default max for the
+ * admin reserve is 8MB. These are usually, but not always, enough to
+ * enable recovery from a memory hogging process using login/sshd, a shell,
+ * and tools like top. It may make sense to increase or even disable the
+ * reserve depending on the existence of swap or variations in the recovery
+ * tools. So, the admin may have changed them.
+ *
+ * If memory is added and the reserves have been eliminated or increased above
+ * the default max, then we'll trust the admin.
+ *
+ * If memory is removed and there isn't enough free memory, then we
+ * need to reset the reserves.
+ *
+ * Otherwise keep the reserve set by the admin.
+ */
+static int reserve_mem_notifier(struct notifier_block *nb,
+                            unsigned long action, void *data)
+{
+       unsigned long tmp, free_kbytes;
+
+       switch (action) {
+       case MEM_ONLINE:
+               /* Default max is 128MB. Leave alone if modified by operator. */
+               tmp = sysctl_user_reserve_kbytes;
+               if (0 < tmp && tmp < (1UL << 17))
+                       init_user_reserve();
+
+               /* Default max is 8MB.  Leave alone if modified by operator. */
+               tmp = sysctl_admin_reserve_kbytes;
+               if (0 < tmp && tmp < (1UL << 13))
+                       init_admin_reserve();
+
+               break;
+       case MEM_OFFLINE:
+               free_kbytes = global_page_state(NR_FREE_PAGES) << (PAGE_SHIFT - 10);
+
+               if (sysctl_user_reserve_kbytes > free_kbytes) {
+                       init_user_reserve();
+                       pr_info("vm.user_reserve_kbytes reset to %lu\n",
+                               sysctl_user_reserve_kbytes);
+               }
+
+               if (sysctl_admin_reserve_kbytes > free_kbytes) {
+                       init_admin_reserve();
+                       pr_info("vm.admin_reserve_kbytes reset to %lu\n",
+                               sysctl_admin_reserve_kbytes);
+               }
+               break;
+       default:
+               break;
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block reserve_mem_nb = {
+       .notifier_call = reserve_mem_notifier,
+};
+
+static int __meminit init_reserve_notifier(void)
+{
+       if (register_hotmemory_notifier(&reserve_mem_nb))
+               printk("Failed registering memory add/remove notifier for admin reserve");
+
+       return 0;
+}
+module_init(init_reserve_notifier)