aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/staging/android/Kconfig8
-rw-r--r--drivers/staging/android/lowmemorykiller.c100
-rw-r--r--fs/exec.c2
-rw-r--r--fs/proc/base.c4
-rw-r--r--include/linux/sched.h10
-rw-r--r--kernel/exit.c1
-rw-r--r--kernel/fork.c1
-rw-r--r--mm/oom_kill.c22
8 files changed, 148 insertions, 0 deletions
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index 4aa1494..30ad4d2 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -99,6 +99,14 @@ config ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
/sys/module/lowmemorykiller/parameters/adj and convert them
to oom_score_adj values.
+config ANDROID_LMK_ADJ_RBTREE
+ bool "Use RBTREE for Android Low Memory Killer"
+ depends on ANDROID_LOW_MEMORY_KILLER
+ default y
+ ---help---
+ Use oom_score_adj rbtree to select the best proecss to kill
+ when system in low memory status.
+
endif # if ANDROID
endmenu
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 5645e29..754057c 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -101,6 +101,12 @@ task_notify_func(struct notifier_block *self, unsigned long val, void *data)
return NOTIFY_OK;
}
+#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE
+static struct task_struct *pick_next_from_adj_tree(struct task_struct *task);
+static struct task_struct *pick_first_task(void);
+static struct task_struct *pick_last_task(void);
+#endif
+
static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
{
struct task_struct *tsk;
@@ -184,7 +190,14 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
selected_oom_score_adj = min_score_adj;
#endif
rcu_read_lock();
+
+#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE
+ for (tsk = pick_first_task();
+ tsk != pick_last_task();
+ tsk = pick_next_from_adj_tree(tsk)) {
+#else
for_each_process(tsk) {
+#endif
struct task_struct *p;
int oom_score_adj;
#ifdef ENHANCED_LMK_ROUTINE
@@ -200,7 +213,11 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
oom_score_adj = p->signal->oom_score_adj;
if (oom_score_adj < min_score_adj) {
task_unlock(p);
+#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE
+ break;
+#else
continue;
+#endif
}
tasksize = get_mm_rss(p->mm);
task_unlock(p);
@@ -246,7 +263,11 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
#else
if (selected) {
if (oom_score_adj < selected_oom_score_adj)
+#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE
+ break;
+#else
continue;
+#endif
if (oom_score_adj == selected_oom_score_adj &&
tasksize <= selected_tasksize)
continue;
@@ -391,6 +412,85 @@ static const struct kparam_array __param_arr_adj = {
};
#endif
+#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE
+DEFINE_SPINLOCK(lmk_lock);
+struct rb_root tasks_scoreadj = RB_ROOT;
+void add_2_adj_tree(struct task_struct *task)
+{
+ struct rb_node **link = &tasks_scoreadj.rb_node;
+ struct rb_node *parent = NULL;
+ struct task_struct *task_entry;
+ s64 key = task->signal->oom_score_adj;
+ /*
+ * Find the right place in the rbtree:
+ */
+ spin_lock(&lmk_lock);
+ while (*link) {
+ parent = *link;
+ task_entry = rb_entry(parent, struct task_struct, adj_node);
+
+ if (key < task_entry->signal->oom_score_adj)
+ link = &parent->rb_right;
+ else
+ link = &parent->rb_left;
+ }
+
+ rb_link_node(&task->adj_node, parent, link);
+ rb_insert_color(&task->adj_node, &tasks_scoreadj);
+ spin_unlock(&lmk_lock);
+}
+
+void delete_from_adj_tree(struct task_struct *task)
+{
+ spin_lock(&lmk_lock);
+ rb_erase(&task->adj_node, &tasks_scoreadj);
+ spin_unlock(&lmk_lock);
+}
+
+
+static struct task_struct *pick_next_from_adj_tree(struct task_struct *task)
+{
+ struct rb_node *next;
+
+ spin_lock(&lmk_lock);
+ next = rb_next(&task->adj_node);
+ spin_unlock(&lmk_lock);
+
+ if (!next)
+ return NULL;
+
+ return rb_entry(next, struct task_struct, adj_node);
+}
+
+static struct task_struct *pick_first_task(void)
+{
+ struct rb_node *left;
+
+ spin_lock(&lmk_lock);
+ left = rb_first(&tasks_scoreadj);
+ spin_unlock(&lmk_lock);
+
+ if (!left)
+ return NULL;
+
+ return rb_entry(left, struct task_struct, adj_node);
+}
+
+static struct task_struct *pick_last_task(void)
+{
+ struct rb_node *right;
+
+ spin_lock(&lmk_lock);
+ right = rb_last(&tasks_scoreadj);
+ spin_unlock(&lmk_lock);
+
+ if (!right)
+ return NULL;
+
+ return rb_entry(right, struct task_struct, adj_node);
+}
+#endif
+
module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR);
#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
__module_param_call(MODULE_PARAM_PREFIX, adj,
diff --git a/fs/exec.c b/fs/exec.c
index f0d744a..8c75690 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -957,6 +957,8 @@ static int de_thread(struct task_struct *tsk)
transfer_pid(leader, tsk, PIDTYPE_SID);
list_replace_rcu(&leader->tasks, &tsk->tasks);
+ delete_from_adj_tree(leader);
+ add_2_adj_tree(tsk);
list_replace_init(&leader->sibling, &tsk->sibling);
tsk->group_leader = tsk;
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 1a140ca..56d23e1 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1063,6 +1063,8 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf,
else
task->signal->oom_score_adj = (oom_adjust * OOM_SCORE_ADJ_MAX) /
-OOM_DISABLE;
+ delete_from_adj_tree(task);
+ add_2_adj_tree(task);
err_sighand:
unlock_task_sighand(task, &flags);
err_task_lock:
@@ -1187,6 +1189,8 @@ static ssize_t oom_score_adj_write(struct file *file, const char __user *buf,
atomic_dec(&task->mm->oom_disable_count);
}
task->signal->oom_score_adj = oom_score_adj;
+ delete_from_adj_tree(task);
+ add_2_adj_tree(task);
if (has_capability_noaudit(current, CAP_SYS_RESOURCE))
task->signal->oom_score_adj_min = oom_score_adj;
/*
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 6d2e888..6b030a5 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1284,6 +1284,9 @@ struct task_struct {
#endif
struct list_head tasks;
+#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE
+ struct rb_node adj_node;
+#endif
#ifdef CONFIG_SMP
struct plist_node pushable_tasks;
#endif
@@ -1623,6 +1626,13 @@ static inline struct pid *task_tgid(struct task_struct *task)
return task->group_leader->pids[PIDTYPE_PID].pid;
}
+#ifdef CONFIG_ANDROID_LMK_ADJ_RBTREE
+extern void add_2_adj_tree(struct task_struct *task);
+extern void delete_from_adj_tree(struct task_struct *task);
+#else
+static inline void add_2_adj_tree(struct task_struct *task) { }
+static inline void delete_from_adj_tree(struct task_struct *task) { }
+#endif
/*
* Without tasklist or rcu lock it is not safe to dereference
* the result of task_pgrp/task_session even if task == current,
diff --git a/kernel/exit.c b/kernel/exit.c
index 97dd317..6b8a7af 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -68,6 +68,7 @@ static void __unhash_process(struct task_struct *p, bool group_dead)
detach_pid(p, PIDTYPE_SID);
list_del_rcu(&p->tasks);
+ delete_from_adj_tree(p);
list_del_init(&p->sibling);
__this_cpu_dec(process_counts);
}
diff --git a/kernel/fork.c b/kernel/fork.c
index 0400fdf..c1760e6 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1379,6 +1379,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
attach_pid(p, PIDTYPE_SID, task_session(current));
list_add_tail(&p->sibling, &p->real_parent->children);
list_add_tail_rcu(&p->tasks, &init_task.tasks);
+ add_2_adj_tree(p);
__this_cpu_inc(process_counts);
}
attach_pid(p, PIDTYPE_PID, pid);
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index 7c72487..678cf2b 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -38,6 +38,28 @@ int sysctl_oom_kill_allocating_task;
int sysctl_oom_dump_tasks = 1;
static DEFINE_SPINLOCK(zone_scan_lock);
+/*
+ * compare_swap_oom_score_adj() - compare and swap current's oom_score_adj
+ * @old_val: old oom_score_adj for compare
+ * @new_val: new oom_score_adj for swap
+ *
+ * Sets the oom_score_adj value for current to @new_val iff its present value is
+ * @old_val. Usually used to reinstate a previous value to prevent racing with
+ * userspacing tuning the value in the interim.
+ */
+void compare_swap_oom_score_adj(int old_val, int new_val)
+{
+ struct sighand_struct *sighand = current->sighand;
+
+ spin_lock_irq(&sighand->siglock);
+ if (current->signal->oom_score_adj == old_val) {
+ current->signal->oom_score_adj = new_val;
+ delete_from_adj_tree(current);
+ add_2_adj_tree(current);
+ }
+ spin_unlock_irq(&sighand->siglock);
+}
+
/**
* test_set_oom_score_adj() - set current's oom_score_adj and return old value
* @new_val: new oom_score_adj value