History

mm, oom: make the calculation of oom badness more accurate

Recently we found an issue on our production environment that when memcg oom is triggered the oom killer doesn't chose the process with largest resident memory but chose the first scanned process. Note that all processes in this memcg have the same oom_score_adj, so the oom killer should chose the process with largest resident memory. Bellow is part of the oom info, which is enough to analyze this issue. [7516987.983223] memory: usage 16777216kB, limit 16777216kB, failcnt 52843037 [7516987.983224] memory+swap: usage 16777216kB, limit 9007199254740988kB, failcnt 0 [7516987.983225] kmem: usage 301464kB, limit 9007199254740988kB, failcnt 0 [...] [7516987.983293] [ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name [7516987.983510] [ 5740] 0 5740 257 1 32768 0 -998 pause [7516987.983574] [58804] 0 58804 4594 771 81920 0 -998 entry_point.bas [7516987.983577] [58908] 0 58908 7089 689 98304 0 -998 cron [7516987.983580] [58910] 0 58910 16235 5576 163840 0 -998 supervisord [7516987.983590] [59620] 0 59620 18074 1395 188416 0 -998 sshd [7516987.983594] [59622] 0 59622 18680 6679 188416 0 -998 python [7516987.983598] [59624] 0 59624 1859266 5161 548864 0 -998 odin-agent [7516987.983600] [59625] 0 59625 707223 9248 983040 0 -998 filebeat [7516987.983604] [59627] 0 59627 416433 64239 774144 0 -998 odin-log-agent [7516987.983607] [59631] 0 59631 180671 15012 385024 0 -998 python3 [7516987.983612] [61396] 0 61396 791287 3189 352256 0 -998 client [7516987.983615] [61641] 0 61641 1844642 29089 946176 0 -998 client [7516987.983765] [ 9236] 0 9236 2642 467 53248 0 -998 php_scanner [7516987.983911] [42898] 0 42898 15543 838 167936 0 -998 su [7516987.983915] [42900] 1000 42900 3673 867 77824 0 -998 exec_script_vr2 [7516987.983918] [42925] 1000 42925 36475 19033 335872 0 -998 python [7516987.983921] [57146] 1000 57146 3673 848 73728 0 -998 exec_script_J2p [7516987.983925] [57195] 1000 57195 186359 22958 491520 0 -998 python2 [7516987.983928] [58376] 1000 58376 275764 14402 290816 0 -998 rosmaster [7516987.983931] [58395] 1000 58395 155166 4449 245760 0 -998 rosout [7516987.983935] [58406] 1000 58406 18285584 3967322 37101568 0 -998 data_sim [7516987.984221] oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=3aa16c9482ae3a6f6b78bda68a55d32c87c99b985e0f11331cddf05af6c4d753,mems_allowed=0-1,oom_memcg=/kubepods/podf1c273d3-9b36-11ea-b3df-246e9693c184,task_memcg=/kubepods/podf1c273d3-9b36-11ea-b3df-246e9693c184/1f246a3eeea8f70bf91141eeaf1805346a666e225f823906485ea0b6c37dfc3d,task=pause,pid=5740,uid=0 [7516987.984254] Memory cgroup out of memory: Killed process 5740 (pause) total-vm:1028kB, anon-rss:4kB, file-rss:0kB, shmem-rss:0kB [7516988.092344] oom_reaper: reaped process 5740 (pause), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB We can find that the first scanned process 5740 (pause) was killed, but its rss is only one page. That is because, when we calculate the oom badness in oom_badness(), we always ignore the negtive point and convert all of these negtive points to 1. Now as oom_score_adj of all the processes in this targeted memcg have the same value -998, the points of these processes are all negtive value. As a result, the first scanned process will be killed. The oom_socre_adj (-998) in this memcg is set by kubelet, because it is a a Guaranteed pod, which has higher priority to prevent from being killed by system oom. To fix this issue, we should make the calculation of oom point more accurate. We can achieve it by convert the chosen_point from 'unsigned long' to 'long'. [cai@lca.pw: reported a issue in the previous version] [mhocko@suse.com: fixed the issue reported by Cai] [mhocko@suse.com: add the comment in proc_oom_score()] [laoar.shao@gmail.com: v3] Link: http://lkml.kernel.org/r/1594396651-9931-1-git-send-email-laoar.shao@gmail.com

Yafang Shao committed 6 years ago

Commit 9066e5c1 parent f3f34163 files · +22 −15Browse files

Unified · Split

3 files
  1. fs/proc/base.c +10 −1
  2. include/linux/oom.h +2 −2
  3. mm/oom_kill.c +10 −12
fs/proc/base.c+10−1
@@ -551,8 +551,17 @@ static int proc_oom_score(struct seq_file *m, struct pid_namespace *ns, · 550 unmodified lines
551{551{
552 unsigned long totalpages = totalram_pages() + total_swap_pages;552 unsigned long totalpages = totalram_pages() + total_swap_pages;
553 unsigned long points = 0;553 unsigned long points = 0;
554 long badness;
555
556 badness = oom_badness(task, totalpages);
557 /*
558 * Special case OOM_SCORE_ADJ_MIN for all others scale the
559 * badness value into [0, 2000] range which we have been
560 * exporting for a long time so userspace might depend on it.
561 */
562 if (badness != LONG_MIN)
563 points = (1000 + badness * 1000 / (long)totalpages) * 2 / 3;
554 564
555 points = oom_badness(task, totalpages) * 1000 / totalpages;
556 seq_printf(m, "%lu\n", points);565 seq_printf(m, "%lu\n", points);
557 566
558 return 0;567 return 0;
include/linux/oom.h+2−2
@@ -48,7 +48,7 @@ struct oom_control { · 47 unmodified lines
48 /* Used by oom implementation, do not set */48 /* Used by oom implementation, do not set */
49 unsigned long totalpages;49 unsigned long totalpages;
50 struct task_struct *chosen;50 struct task_struct *chosen;
51 unsigned long chosen_points;51 long chosen_points;
52 52
53 /* Used to print the constraint info. */53 /* Used to print the constraint info. */
54 enum oom_constraint constraint;54 enum oom_constraint constraint;
@@ -107,7 +107,7 @@ static inline vm_fault_t check_stable_address_space(struct mm_struct *mm) · 52 unmodified lines
107 107
108bool __oom_reap_task_mm(struct mm_struct *mm);108bool __oom_reap_task_mm(struct mm_struct *mm);
109 109
110extern unsigned long oom_badness(struct task_struct *p,110long oom_badness(struct task_struct *p,
111 unsigned long totalpages);111 unsigned long totalpages);
112 112
113extern bool out_of_memory(struct oom_control *oc);113extern bool out_of_memory(struct oom_control *oc);
mm/oom_kill.c+10−12
@@ -196,17 +196,17 @@ static bool is_dump_unreclaim_slabs(void) · 195 unmodified lines
196 * predictable as possible. The goal is to return the highest value for the196 * predictable as possible. The goal is to return the highest value for the
197 * task consuming the most memory to avoid subsequent oom failures.197 * task consuming the most memory to avoid subsequent oom failures.
198 */198 */
199unsigned long oom_badness(struct task_struct *p, unsigned long totalpages)199long oom_badness(struct task_struct *p, unsigned long totalpages)
200{200{
201 long points;201 long points;
202 long adj;202 long adj;
203 203
204 if (oom_unkillable_task(p))204 if (oom_unkillable_task(p))
205 return 0;205 return LONG_MIN;
206 206
207 p = find_lock_task_mm(p);207 p = find_lock_task_mm(p);
208 if (!p)208 if (!p)
209 return 0;209 return LONG_MIN;
210 210
211 /*211 /*
212 * Do not even consider tasks which are explicitly marked oom212 * Do not even consider tasks which are explicitly marked oom
@@ -218,7 +218,7 @@ unsigned long oom_badness(struct task_struct *p, unsigned long totalpages) · 5 unmodified lines
218 test_bit(MMF_OOM_SKIP, &p->mm->flags) ||218 test_bit(MMF_OOM_SKIP, &p->mm->flags) ||
219 in_vfork(p)) {219 in_vfork(p)) {
220 task_unlock(p);220 task_unlock(p);
221 return 0;221 return LONG_MIN;
222 }222 }
223 223
224 /*224 /*
@@ -233,11 +233,7 @@ unsigned long oom_badness(struct task_struct *p, unsigned long totalpages) · 8 unmodified lines
233 adj *= totalpages / 1000;233 adj *= totalpages / 1000;
234 points += adj;234 points += adj;
235 235
236 /*236 return points;
237 * Never return 0 for an eligible task regardless of the root bonus and
238 * oom_score_adj (oom_score_adj can't be OOM_SCORE_ADJ_MIN here).
239 */
240 return points > 0 ? points : 1;
241}237}
242 238
243static const char * const oom_constraint_text[] = {239static const char * const oom_constraint_text[] = {
@@ -310,7 +306,7 @@ static enum oom_constraint constrained_alloc(struct oom_control *oc) · 66 unmodified lines
310static int oom_evaluate_task(struct task_struct *task, void *arg)306static int oom_evaluate_task(struct task_struct *task, void *arg)
311{307{
312 struct oom_control *oc = arg;308 struct oom_control *oc = arg;
313 unsigned long points;309 long points;
314 310
315 if (oom_unkillable_task(task))311 if (oom_unkillable_task(task))
316 goto next;312 goto next;
@@ -336,12 +332,12 @@ static int oom_evaluate_task(struct task_struct *task, void *arg) · 19 unmodified lines
336 * killed first if it triggers an oom, then select it.332 * killed first if it triggers an oom, then select it.
337 */333 */
338 if (oom_task_origin(task)) {334 if (oom_task_origin(task)) {
339 points = ULONG_MAX;335 points = LONG_MAX;
340 goto select;336 goto select;
341 }337 }
342 338
343 points = oom_badness(task, oc->totalpages);339 points = oom_badness(task, oc->totalpages);
344 if (!points || points < oc->chosen_points)340 if (points == LONG_MIN || points < oc->chosen_points)
345 goto next;341 goto next;
346 342
347select:343select:
@@ -365,6 +361,8 @@ abort: · 17 unmodified lines
365 */361 */
366static void select_bad_process(struct oom_control *oc)362static void select_bad_process(struct oom_control *oc)
367{363{
364 oc->chosen_points = LONG_MIN;
365
368 if (is_memcg_oom(oc))366 if (is_memcg_oom(oc))
369 mem_cgroup_scan_tasks(oc->memcg, oom_evaluate_task, oc);367 mem_cgroup_scan_tasks(oc->memcg, oom_evaluate_task, oc);
370 else {368 else {

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t