aboutsummaryrefslogtreecommitdiffstats
path: root/fs/proc
diff options
context:
space:
mode:
authorDavid Rientjes <rientjes@google.com>2010-08-09 20:19:46 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-08-09 23:45:02 -0400
commita63d83f427fbce97a6cea0db2e64b0eb8435cd10 (patch)
tree8ac229cdf6e2289d97e82e35774057106fe7f4a2 /fs/proc
parent74bcbf40546bb7500f2a7ba4ff3cc056a6bd004a (diff)
oom: badness heuristic rewrite
This a complete rewrite of the oom killer's badness() heuristic which is used to determine which task to kill in oom conditions. The goal is to make it as simple and predictable as possible so the results are better understood and we end up killing the task which will lead to the most memory freeing while still respecting the fine-tuning from userspace. Instead of basing the heuristic on mm->total_vm for each task, the task's rss and swap space is used instead. This is a better indication of the amount of memory that will be freeable if the oom killed task is chosen and subsequently exits. This helps specifically in cases where KDE or GNOME is chosen for oom kill on desktop systems instead of a memory hogging task. The baseline for the heuristic is a proportion of memory that each task is currently using in memory plus swap compared to the amount of "allowable" memory. "Allowable," in this sense, means the system-wide resources for unconstrained oom conditions, the set of mempolicy nodes, the mems attached to current's cpuset, or a memory controller's limit. The proportion is given on a scale of 0 (never kill) to 1000 (always kill), roughly meaning that if a task has a badness() score of 500 that the task consumes approximately 50% of allowable memory resident in RAM or in swap space. The proportion is always relative to the amount of "allowable" memory and not the total amount of RAM systemwide so that mempolicies and cpusets may operate in isolation; they shall not need to know the true size of the machine on which they are running if they are bound to a specific set of nodes or mems, respectively. Root tasks are given 3% extra memory just like __vm_enough_memory() provides in LSMs. In the event of two tasks consuming similar amounts of memory, it is generally better to save root's task. Because of the change in the badness() heuristic's baseline, it is also necessary to introduce a new user interface to tune it. It's not possible to redefine the meaning of /proc/pid/oom_adj with a new scale since the ABI cannot be changed for backward compatability. Instead, a new tunable, /proc/pid/oom_score_adj, is added that ranges from -1000 to +1000. It may be used to polarize the heuristic such that certain tasks are never considered for oom kill while others may always be considered. The value is added directly into the badness() score so a value of -500, for example, means to discount 50% of its memory consumption in comparison to other tasks either on the system, bound to the mempolicy, in the cpuset, or sharing the same memory controller. /proc/pid/oom_adj is changed so that its meaning is rescaled into the units used by /proc/pid/oom_score_adj, and vice versa. Changing one of these per-task tunables will rescale the value of the other to an equivalent meaning. Although /proc/pid/oom_adj was originally defined as a bitshift on the badness score, it now shares the same linear growth as /proc/pid/oom_score_adj but with different granularity. This is required so the ABI is not broken with userspace applications and allows oom_adj to be deprecated for future removal. Signed-off-by: David Rientjes <rientjes@google.com> Cc: Nick Piggin <npiggin@suse.de> Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Balbir Singh <balbir@in.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/proc')
-rw-r--r--fs/proc/base.c94
1 files changed, 90 insertions, 4 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 5949d0ac30f2..f923b728388a 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -63,6 +63,7 @@
63#include <linux/namei.h> 63#include <linux/namei.h>
64#include <linux/mnt_namespace.h> 64#include <linux/mnt_namespace.h>
65#include <linux/mm.h> 65#include <linux/mm.h>
66#include <linux/swap.h>
66#include <linux/rcupdate.h> 67#include <linux/rcupdate.h>
67#include <linux/kallsyms.h> 68#include <linux/kallsyms.h>
68#include <linux/stacktrace.h> 69#include <linux/stacktrace.h>
@@ -430,12 +431,11 @@ static const struct file_operations proc_lstats_operations = {
430static int proc_oom_score(struct task_struct *task, char *buffer) 431static int proc_oom_score(struct task_struct *task, char *buffer)
431{ 432{
432 unsigned long points = 0; 433 unsigned long points = 0;
433 struct timespec uptime;
434 434
435 do_posix_clock_monotonic_gettime(&uptime);
436 read_lock(&tasklist_lock); 435 read_lock(&tasklist_lock);
437 if (pid_alive(task)) 436 if (pid_alive(task))
438 points = badness(task, NULL, NULL, uptime.tv_sec); 437 points = oom_badness(task, NULL, NULL,
438 totalram_pages + total_swap_pages);
439 read_unlock(&tasklist_lock); 439 read_unlock(&tasklist_lock);
440 return sprintf(buffer, "%lu\n", points); 440 return sprintf(buffer, "%lu\n", points);
441} 441}
@@ -1038,7 +1038,15 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf,
1038 } 1038 }
1039 1039
1040 task->signal->oom_adj = oom_adjust; 1040 task->signal->oom_adj = oom_adjust;
1041 1041 /*
1042 * Scale /proc/pid/oom_score_adj appropriately ensuring that a maximum
1043 * value is always attainable.
1044 */
1045 if (task->signal->oom_adj == OOM_ADJUST_MAX)
1046 task->signal->oom_score_adj = OOM_SCORE_ADJ_MAX;
1047 else
1048 task->signal->oom_score_adj = (oom_adjust * OOM_SCORE_ADJ_MAX) /
1049 -OOM_DISABLE;
1042 unlock_task_sighand(task, &flags); 1050 unlock_task_sighand(task, &flags);
1043 put_task_struct(task); 1051 put_task_struct(task);
1044 1052
@@ -1051,6 +1059,82 @@ static const struct file_operations proc_oom_adjust_operations = {
1051 .llseek = generic_file_llseek, 1059 .llseek = generic_file_llseek,
1052}; 1060};
1053 1061
1062static ssize_t oom_score_adj_read(struct file *file, char __user *buf,
1063 size_t count, loff_t *ppos)
1064{
1065 struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
1066 char buffer[PROC_NUMBUF];
1067 int oom_score_adj = OOM_SCORE_ADJ_MIN;
1068 unsigned long flags;
1069 size_t len;
1070
1071 if (!task)
1072 return -ESRCH;
1073 if (lock_task_sighand(task, &flags)) {
1074 oom_score_adj = task->signal->oom_score_adj;
1075 unlock_task_sighand(task, &flags);
1076 }
1077 put_task_struct(task);
1078 len = snprintf(buffer, sizeof(buffer), "%d\n", oom_score_adj);
1079 return simple_read_from_buffer(buf, count, ppos, buffer, len);
1080}
1081
1082static ssize_t oom_score_adj_write(struct file *file, const char __user *buf,
1083 size_t count, loff_t *ppos)
1084{
1085 struct task_struct *task;
1086 char buffer[PROC_NUMBUF];
1087 unsigned long flags;
1088 long oom_score_adj;
1089 int err;
1090
1091 memset(buffer, 0, sizeof(buffer));
1092 if (count > sizeof(buffer) - 1)
1093 count = sizeof(buffer) - 1;
1094 if (copy_from_user(buffer, buf, count))
1095 return -EFAULT;
1096
1097 err = strict_strtol(strstrip(buffer), 0, &oom_score_adj);
1098 if (err)
1099 return -EINVAL;
1100 if (oom_score_adj < OOM_SCORE_ADJ_MIN ||
1101 oom_score_adj > OOM_SCORE_ADJ_MAX)
1102 return -EINVAL;
1103
1104 task = get_proc_task(file->f_path.dentry->d_inode);
1105 if (!task)
1106 return -ESRCH;
1107 if (!lock_task_sighand(task, &flags)) {
1108 put_task_struct(task);
1109 return -ESRCH;
1110 }
1111 if (oom_score_adj < task->signal->oom_score_adj &&
1112 !capable(CAP_SYS_RESOURCE)) {
1113 unlock_task_sighand(task, &flags);
1114 put_task_struct(task);
1115 return -EACCES;
1116 }
1117
1118 task->signal->oom_score_adj = oom_score_adj;
1119 /*
1120 * Scale /proc/pid/oom_adj appropriately ensuring that OOM_DISABLE is
1121 * always attainable.
1122 */
1123 if (task->signal->oom_score_adj == OOM_SCORE_ADJ_MIN)
1124 task->signal->oom_adj = OOM_DISABLE;
1125 else
1126 task->signal->oom_adj = (oom_score_adj * OOM_ADJUST_MAX) /
1127 OOM_SCORE_ADJ_MAX;
1128 unlock_task_sighand(task, &flags);
1129 put_task_struct(task);
1130 return count;
1131}
1132
1133static const struct file_operations proc_oom_score_adj_operations = {
1134 .read = oom_score_adj_read,
1135 .write = oom_score_adj_write,
1136};
1137
1054#ifdef CONFIG_AUDITSYSCALL 1138#ifdef CONFIG_AUDITSYSCALL
1055#define TMPBUFLEN 21 1139#define TMPBUFLEN 21
1056static ssize_t proc_loginuid_read(struct file * file, char __user * buf, 1140static ssize_t proc_loginuid_read(struct file * file, char __user * buf,
@@ -2623,6 +2707,7 @@ static const struct pid_entry tgid_base_stuff[] = {
2623#endif 2707#endif
2624 INF("oom_score", S_IRUGO, proc_oom_score), 2708 INF("oom_score", S_IRUGO, proc_oom_score),
2625 REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adjust_operations), 2709 REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adjust_operations),
2710 REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),
2626#ifdef CONFIG_AUDITSYSCALL 2711#ifdef CONFIG_AUDITSYSCALL
2627 REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations), 2712 REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
2628 REG("sessionid", S_IRUGO, proc_sessionid_operations), 2713 REG("sessionid", S_IRUGO, proc_sessionid_operations),
@@ -2957,6 +3042,7 @@ static const struct pid_entry tid_base_stuff[] = {
2957#endif 3042#endif
2958 INF("oom_score", S_IRUGO, proc_oom_score), 3043 INF("oom_score", S_IRUGO, proc_oom_score),
2959 REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adjust_operations), 3044 REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adjust_operations),
3045 REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations),
2960#ifdef CONFIG_AUDITSYSCALL 3046#ifdef CONFIG_AUDITSYSCALL
2961 REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations), 3047 REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations),
2962 REG("sessionid", S_IRUSR, proc_sessionid_operations), 3048 REG("sessionid", S_IRUSR, proc_sessionid_operations),