lowmemorykiller: sysfs node and notifications
Create node "/sys/kernel/mm/lowmemkiller/notify_trigger_active",
on which an userspace application can poll for notification when
the number of free physical memory pages in the system falls below
a set threshold. The threshold is set by the new module parameter
"/sys/module/lowmemorykiller/parameters/notify_trigger".
The threshold should be typically set to be higher than the largest
free pages value in /sys/module/lowmemorykiller/parameters/minfree,
so that notification happens before any processes get killed.
Change-Id: Iccb2d1ff699e34b2c22687997e428eec27787b82
Signed-off-by: Manjunatha Madana <mamanj@codeaurora.org>
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index 8f548691..9784f5f 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -42,6 +42,17 @@
a 'cuttlefish' Android image inside QEmu. The driver interacts with
a QEmu ivshmem device. If built as a module, it will be called vsoc.
+config ANDROID_LMK_NOTIFY_TRIGGER
+ bool "Android Low Memory Killer Notify Trigger"
+ depends on ANDROID_LOW_MEMORY_KILLER
+ default n
+ ---help---
+ Create node "/sys/kernel/mm/lowmemkiller/notify_trigger_active",
+ on which an userspace application can poll for notification when
+ the number of free physical memory pages in the system falls below
+ a set threshold. The threshold is set by the new module parameter
+ "/sys/module/lowmemorykiller/parameters/notify_trigger".
+
source "drivers/staging/android/ion/Kconfig"
endif # if ANDROID
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 3973822..853601d 100755
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -92,6 +92,12 @@
static unsigned long lowmem_deathpending_timeout;
+#ifdef CONFIG_ANDROID_LMK_NOTIFY_TRIGGER
+static struct shrink_control lowmem_notif_sc = {GFP_KERNEL, 0};
+static int lowmem_minfree_notif_trigger;
+static struct kobject *lowmem_notify_kobj;
+#endif
+
#define lowmem_print(level, x...) \
do { \
if (lowmem_debug_level >= (level)) \
@@ -582,6 +588,34 @@
}
}
+#ifdef CONFIG_ANDROID_LMK_NOTIFY_TRIGGER
+static void lowmem_notify_killzone_approach(void);
+
+static int get_free_ram(int *other_free, int *other_file,
+ struct shrink_control *sc)
+{
+ *other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
+
+ if (global_node_page_state(NR_SHMEM) + total_swapcache_pages() +
+ global_node_page_state(NR_UNEVICTABLE) <
+ global_node_page_state(NR_FILE_PAGES))
+ *other_file = global_node_page_state(NR_FILE_PAGES) -
+ global_node_page_state(NR_SHMEM) -
+ global_node_page_state(NR_UNEVICTABLE) -
+ total_swapcache_pages();
+ else
+ *other_file = 0;
+
+ tune_lmk_param(other_free, other_file, sc);
+
+ if (*other_free < lowmem_minfree_notif_trigger &&
+ *other_file < lowmem_minfree_notif_trigger)
+ return 1;
+ else
+ return 0;
+}
+#endif
+
static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
{
struct task_struct *tsk;
@@ -601,6 +635,12 @@
if (!mutex_trylock(&scan_mutex))
return 0;
+#ifdef CONFIG_ANDROID_LMK_NOTIFY_TRIGGER
+ lowmem_notif_sc.gfp_mask = sc->gfp_mask;
+
+ if (get_free_ram(&other_free, &other_file, sc))
+ lowmem_notify_killzone_approach();
+#else
other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
if (global_node_page_state(NR_SHMEM) + total_swapcache_pages() +
@@ -614,6 +654,7 @@
other_file = 0;
tune_lmk_param(&other_free, &other_file, sc);
+#endif
if (lowmem_adj_size < array_size)
array_size = lowmem_adj_size;
@@ -801,8 +842,74 @@
.seeks = DEFAULT_SEEKS * 16
};
+#ifdef CONFIG_ANDROID_LMK_NOTIFY_TRIGGER
+static void lowmem_notify_killzone_approach(void)
+{
+ lowmem_print(3, "notification trigger activated\n");
+ sysfs_notify(lowmem_notify_kobj, NULL,
+ "notify_trigger_active");
+}
+
+static ssize_t lowmem_notify_trigger_active_show(struct kobject *k,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ int other_free, other_file;
+
+ if (get_free_ram(&other_free, &other_file, &lowmem_notif_sc))
+ return snprintf(buf, 3, "1\n");
+ else
+ return snprintf(buf, 3, "0\n");
+}
+
+static struct kobj_attribute lowmem_notify_trigger_active_attr =
+ __ATTR(notify_trigger_active, 0444,
+ lowmem_notify_trigger_active_show, NULL);
+
+static struct attribute *lowmem_notify_default_attrs[] = {
+ &lowmem_notify_trigger_active_attr.attr, NULL,
+};
+
+static ssize_t lowmem_show(struct kobject *k, struct attribute *attr, char *buf)
+{
+ struct kobj_attribute *kobj_attr;
+
+ kobj_attr = container_of(attr, struct kobj_attribute, attr);
+ return kobj_attr->show(k, kobj_attr, buf);
+}
+
+static const struct sysfs_ops lowmem_notify_ops = {
+ .show = lowmem_show,
+};
+
+static void lowmem_notify_kobj_release(struct kobject *kobj)
+{
+ /* Nothing to be done here */
+}
+
+static struct kobj_type lowmem_notify_kobj_type = {
+ .release = lowmem_notify_kobj_release,
+ .sysfs_ops = &lowmem_notify_ops,
+ .default_attrs = lowmem_notify_default_attrs,
+};
+#endif
+
static int __init lowmem_init(void)
{
+#ifdef CONFIG_ANDROID_LMK_NOTIFY_TRIGGER
+ int rc;
+
+ lowmem_notify_kobj = kzalloc(sizeof(*lowmem_notify_kobj), GFP_KERNEL);
+ if (!lowmem_notify_kobj)
+ return -ENOMEM;
+
+ rc = kobject_init_and_add(lowmem_notify_kobj, &lowmem_notify_kobj_type,
+ mm_kobj, "lowmemkiller");
+ if (rc) {
+ kfree(lowmem_notify_kobj);
+ return rc;
+ }
+#endif
register_shrinker(&lowmem_shrinker);
vmpressure_notifier_register(&lmk_vmpr_nb);
lmk_event_init();
@@ -904,4 +1011,6 @@
S_IRUGO | S_IWUSR);
module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
module_param_named(lmk_fast_run, lmk_fast_run, int, S_IRUGO | S_IWUSR);
-
+#ifdef CONFIG_ANDROID_LMK_NOTIFY_TRIGGER
+module_param_named(notify_trigger, lowmem_minfree_notif_trigger, uint, 0644);
+#endif