aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHong-Mei Li <a21834@motorola.com>2013-06-28 19:26:38 +0800
committerSimon Shields <keepcalm444@gmail.com>2016-06-13 14:47:39 +1000
commit6f04da23b1e3aa60626bfef868ee89a77cebd637 (patch)
tree965631521a4d3b9831846da116efda37ddeae69b
parent8f5f33a7fd66e85d8d8502b5ee9c1c731ff0aeb5 (diff)
downloadkernel_samsung_smdk4412-6f04da23b1e3aa60626bfef868ee89a77cebd637.zip
kernel_samsung_smdk4412-6f04da23b1e3aa60626bfef868ee89a77cebd637.tar.gz
kernel_samsung_smdk4412-6f04da23b1e3aa60626bfef868ee89a77cebd637.tar.bz2
staging: android: lowmemorykiller: implement task's adj rbtree
Based on the current LMK implementation, LMK has to scan all processes to select the correct task to kill during low memory. The basic idea for the optimization is to : queue all tasks with oom_score_adj priority, and then LMK just selects the proper task from the queue(rbtree) to kill. performance improvement: the current implementation: average time to find a task to kill : 1004us the optimized implementation: average time to find a task to kill: 43us Change-Id: I4dbbdd5673314dbbdabb71c3eff0dc229ce4ea91 Signed-off-by: Hong-Mei Li <a21834@motorola.com> Reviewed-on: http://gerrit.pcs.mot.com/548917 SLT-Approved: Slta Waiver <sltawvr@motorola.com> Tested-by: Jira Key <jirakey@motorola.com> Reviewed-by: Yi-Wei Zhao <gbjc64@motorola.com> Submit-Approved: Jira Key <jirakey@motorola.com> Signed-off-by: D. Andrei Măceș <dmaces@nd.edu> Conflicts: drivers/staging/android/Kconfig drivers/staging/android/lowmemorykiller.c fs/proc/base.c mm/oom_kill.c Conflicts: drivers/staging/android/lowmemorykiller.c mm/oom_kill.c Conflicts: mm/oom_kill.c Conflicts: drivers/staging/android/lowmemorykiller.c mm/oom_kill.c
-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