|
@@ -33,6 +33,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>
|
|
@@ -3110,7 +3112,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;
|
|
|
|
|
@@ -3131,7 +3133,7 @@ module_init(init_user_reserve)
|
|
|
* with sshd, bash, and top in OVERCOMMIT_GUESS. Smaller systems will
|
|
|
* only reserve 3% of free pages by default.
|
|
|
*/
|
|
|
-static int __meminit init_admin_reserve(void)
|
|
|
+static int init_admin_reserve(void)
|
|
|
{
|
|
|
unsigned long free_kbytes;
|
|
|
|
|
@@ -3141,3 +3143,73 @@ static int __meminit init_admin_reserve(void)
|
|
|
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)
|