staging: android: lowmemorykiller: sysfs node and notifications

android, lowmemorykiller: remove task handoff notifier
staging: android: lowmemorykiller: Fix task_struct leak
staging: android/lowmemorykiller: Don't unregister notifier from atomic context
staging: android, lowmemorykiller: convert to use oom_score_adj
staging: android/lowmemorykiller: Do not kill kernel threads
staging: android/lowmemorykiller: No need for task->signal check
staging: android/lowmemorykiller: Better mm handling
staging: android/lowmemorykiller: Don't grab tasklist_lock
staging: android: lowmemorykiller: Don't wait more than one second for a process to die
Staging: android: fixed 80 characters warnings in lowmemorykiller.c
staging: android: lowmemorykiller: Ignore shmem pages in page-cache
staging: android: lowmemorykiller: Remove bitrotted codepath
staging: android: lowmemkiller: Substantially reduce overhead during reclaim
staging: android: lowmemorykiller: Don't try to kill the same pid over and over
This commit is contained in:
securecrt 2012-08-15 13:37:30 +08:00
parent 9b8229cafb
commit d6b41b0def

201
drivers/staging/android/lowmemorykiller.c Normal file → Executable file
View File

@ -29,12 +29,17 @@
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/memory.h>
#include <linux/memory_hotplug.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/oom.h>
#include <linux/sched.h>
#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
static uint32_t lowmem_debug_level = 2;
static int lowmem_adj[6] = {
@ -52,8 +57,12 @@ static size_t lowmem_minfree[6] = {
};
static int lowmem_minfree_size = 4;
static size_t lowmem_minfree_notif_trigger;
static unsigned int offlining;
static struct task_struct *lowmem_deathpending;
static DEFINE_SPINLOCK(lowmem_deathpending_lock);
static unsigned long lowmem_deathpending_timeout;
static struct kobject *lowmem_kobj;
#define lowmem_print(level, x...) \
do { \
@ -68,29 +77,66 @@ static struct notifier_block task_nb = {
.notifier_call = task_notify_func,
};
static void task_free_fn(struct work_struct *work)
{
unsigned long flags;
task_free_unregister(&task_nb);
spin_lock_irqsave(&lowmem_deathpending_lock, flags);
lowmem_deathpending = NULL;
spin_unlock_irqrestore(&lowmem_deathpending_lock, flags);
}
static DECLARE_WORK(task_free_work, task_free_fn);
static int
task_notify_func(struct notifier_block *self, unsigned long val, void *data)
{
struct task_struct *task = data;
if (task == lowmem_deathpending) {
schedule_work(&task_free_work);
}
if (task == lowmem_deathpending)
lowmem_deathpending = NULL;
return NOTIFY_OK;
}
#ifdef CONFIG_MEMORY_HOTPLUG
static int lmk_hotplug_callback(struct notifier_block *self,
unsigned long cmd, void *data)
{
switch (cmd) {
/* Don't care LMK cases */
case MEM_ONLINE:
case MEM_OFFLINE:
case MEM_CANCEL_ONLINE:
case MEM_CANCEL_OFFLINE:
case MEM_GOING_ONLINE:
offlining = 0;
lowmem_print(4, "lmk in normal mode\n");
break;
/* LMK should account for movable zone */
case MEM_GOING_OFFLINE:
offlining = 1;
lowmem_print(4, "lmk in hotplug mode\n");
break;
}
return NOTIFY_DONE;
}
#endif
static void lowmem_notify_killzone_approach(void);
static inline void get_free_ram(int *other_free, int *other_file)
{
struct zone *zone;
*other_free = global_page_state(NR_FREE_PAGES);
*other_file = global_page_state(NR_FILE_PAGES) -
global_page_state(NR_SHMEM);
if (offlining) {
/* Discount all free space in the section being offlined */
for_each_zone(zone) {
if (zone_idx(zone) == ZONE_MOVABLE) {
*other_free -= zone_page_state(zone,
NR_FREE_PAGES);
lowmem_print(4, "lowmem_shrink discounted "
"%lu pages in movable zone\n",
zone_page_state(zone, NR_FREE_PAGES));
}
}
}
}
static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
{
struct task_struct *p;
@ -102,10 +148,8 @@ static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
int selected_tasksize = 0;
int selected_oom_adj;
int array_size = ARRAY_SIZE(lowmem_adj);
int other_free = global_page_state(NR_FREE_PAGES);
int other_file = global_page_state(NR_FILE_PAGES);
unsigned long flags;
int other_free;
int other_file;
/*
* If we already have a death outstanding, then
* bail out right away; indicating to vmscan
@ -113,15 +157,24 @@ static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
* this pass.
*
*/
if (lowmem_deathpending)
if (lowmem_deathpending &&
time_before_eq(jiffies, lowmem_deathpending_timeout))
return 0;
get_free_ram(&other_free, &other_file);
if (other_free < lowmem_minfree_notif_trigger &&
other_file < lowmem_minfree_notif_trigger) {
lowmem_notify_killzone_approach();
}
if (lowmem_adj_size < array_size)
array_size = lowmem_adj_size;
if (lowmem_minfree_size < array_size)
array_size = lowmem_minfree_size;
for (i = 0; i < array_size; i++) {
if (other_file < lowmem_minfree[i]) {
if (other_free < lowmem_minfree[i] &&
other_file < lowmem_minfree[i]) {
min_adj = lowmem_adj[i];
break;
}
@ -176,20 +229,14 @@ static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
p->pid, p->comm, oom_adj, tasksize);
}
if (selected) {
spin_lock_irqsave(&lowmem_deathpending_lock, flags);
if (!lowmem_deathpending) {
lowmem_print(1,
"send sigkill to %d (%s), adj %d, size %d\n",
selected->pid, selected->comm,
selected_oom_adj, selected_tasksize);
lowmem_deathpending = selected;
task_free_register(&task_nb);
force_sig(SIGKILL, selected);
rem -= selected_tasksize;
}
spin_unlock_irqrestore(&lowmem_deathpending_lock, flags);
lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
selected->pid, selected->comm,
selected_oom_adj, selected_tasksize);
lowmem_deathpending = selected;
lowmem_deathpending_timeout = jiffies + HZ;
force_sig(SIGKILL, selected);
rem -= selected_tasksize;
}
lowmem_print(4, "lowmem_shrink %d, %x, return %d\n",
nr_to_scan, gfp_mask, rem);
@ -202,15 +249,93 @@ static struct shrinker lowmem_shrinker = {
.seeks = DEFAULT_SEEKS * 16
};
static void lowmem_notify_killzone_approach(void)
{
lowmem_print(3, "notification trigger activated\n");
sysfs_notify(lowmem_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;
get_free_ram(&other_free, &other_file);
if (other_free < lowmem_minfree_notif_trigger &&
other_file < lowmem_minfree_notif_trigger)
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, S_IRUGO,
lowmem_notify_trigger_active_show, NULL);
static struct attribute *lowmem_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_ops = {
.show = lowmem_show,
};
static void lowmem_kobj_release(struct kobject *kobj)
{
/* Nothing to be done here */
}
static struct kobj_type lowmem_kobj_type = {
.release = lowmem_kobj_release,
.sysfs_ops = &lowmem_ops,
.default_attrs = lowmem_default_attrs,
};
static int __init lowmem_init(void)
{
int rc;
task_free_register(&task_nb);
register_shrinker(&lowmem_shrinker);
#ifdef CONFIG_MEMORY_HOTPLUG
hotplug_memory_notifier(lmk_hotplug_callback, 0);
#endif
lowmem_kobj = kzalloc(sizeof(*lowmem_kobj), GFP_KERNEL);
if (!lowmem_kobj) {
rc = -ENOMEM;
goto err;
}
rc = kobject_init_and_add(lowmem_kobj, &lowmem_kobj_type,
mm_kobj, "lowmemkiller");
if (rc)
goto err_kobj;
return 0;
err_kobj:
kfree(lowmem_kobj);
err:
unregister_shrinker(&lowmem_shrinker);
task_free_unregister(&task_nb);
return rc;
}
static void __exit lowmem_exit(void)
{
kobject_put(lowmem_kobj);
kfree(lowmem_kobj);
unregister_shrinker(&lowmem_shrinker);
task_free_unregister(&task_nb);
}
module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR);
@ -219,6 +344,8 @@ module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size,
module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size,
S_IRUGO | S_IWUSR);
module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
module_param_named(notify_trigger, lowmem_minfree_notif_trigger, uint,
S_IRUGO | S_IWUSR);
module_init(lowmem_init);
module_exit(lowmem_exit);