diff options
-rw-r--r-- | drivers/staging/android/Kconfig | 8 | ||||
-rw-r--r-- | drivers/staging/android/lowmemorykiller.c | 100 | ||||
-rw-r--r-- | fs/exec.c | 2 | ||||
-rw-r--r-- | fs/proc/base.c | 4 | ||||
-rw-r--r-- | include/linux/sched.h | 10 | ||||
-rw-r--r-- | kernel/exit.c | 1 | ||||
-rw-r--r-- | kernel/fork.c | 1 | ||||
-rw-r--r-- | mm/oom_kill.c | 22 |
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, @@ -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 |