[PATCH] mm/oom: Add killed process selection information

From: Edward Chron
Date: Thu Aug 08 2019 - 14:42:03 EST


For an OOM event: print oomscore, memory pct, oom adjustment of the process
that OOM kills and the totalpages value in kB (KiB) used in the calculation
with the OOM killed process message. This is helpful to document why the
process was selected by OOM at the time of the OOM event.

Sample message output:
Jul 21 20:07:48 yoursystem kernel: Out of memory: Killed process 2826
(processname) total-vm:1056800kB, anon-rss:1052784kB, file-rss:4kB,
shmem-rss:0kB memory-usage:3.2% oom_score:1032 oom_score_adj:1000
total-pages: 32791748kB

Signed-off-by: Edward Chron <echron@xxxxxxxxxx>
---
fs/proc/base.c | 2 +-
include/linux/oom.h | 18 +++++++++++-
mm/oom_kill.c | 67 +++++++++++++++++++++++++++++++++------------
3 files changed, 68 insertions(+), 19 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index ebea9501afb8..41880990e6a8 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -550,7 +550,7 @@ static int proc_oom_score(struct seq_file *m, struct pid_namespace *ns,
unsigned long totalpages = totalram_pages() + total_swap_pages;
unsigned long points = 0;

- points = oom_badness(task, totalpages) * 1000 / totalpages;
+ points = oom_badness(task, totalpages, NULL) * 1000 / totalpages;
seq_printf(m, "%lu\n", points);

return 0;
diff --git a/include/linux/oom.h b/include/linux/oom.h
index c696c265f019..7f7ab125c21c 100644
--- a/include/linux/oom.h
+++ b/include/linux/oom.h
@@ -49,6 +49,8 @@ struct oom_control {
unsigned long totalpages;
struct task_struct *chosen;
unsigned long chosen_points;
+ unsigned long chosen_mempts;
+ unsigned long chosen_adj;

/* Used to print the constraint info. */
enum oom_constraint constraint;
@@ -105,10 +107,24 @@ static inline vm_fault_t check_stable_address_space(struct mm_struct *mm)
return 0;
}

+/*
+ * Optional argument that can be passed to oom_badness in the arg field
+ *
+ * Input fields that can be filled in: memcg and nodemask
+ * Output fields that can be returned: mempts, adj
+ */
+struct oom_bad_parms {
+ struct mem_cgroup *memcg;
+ const nodemask_t *nodemask;
+ unsigned long mempts;
+ long adj;
+};
+
bool __oom_reap_task_mm(struct mm_struct *mm);

extern unsigned long oom_badness(struct task_struct *p,
- unsigned long totalpages);
+ unsigned long totalpages,
+ struct oom_bad_parms *obp);

extern bool out_of_memory(struct oom_control *oc);

diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index eda2e2a0bdc6..0548845dbef8 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -42,6 +42,7 @@
#include <linux/kthread.h>
#include <linux/init.h>
#include <linux/mmu_notifier.h>
+#include <linux/oom.h>

#include <asm/tlb.h>
#include "internal.h"
@@ -195,7 +196,8 @@ static bool is_dump_unreclaim_slabs(void)
* predictable as possible. The goal is to return the highest value for the
* task consuming the most memory to avoid subsequent oom failures.
*/
-unsigned long oom_badness(struct task_struct *p, unsigned long totalpages)
+unsigned long oom_badness(struct task_struct *p, unsigned long totalpages,
+ struct oom_bad_parms *obp)
{
long points;
long adj;
@@ -208,15 +210,16 @@ unsigned long oom_badness(struct task_struct *p, unsigned long totalpages)
return 0;

/*
- * Do not even consider tasks which are explicitly marked oom
- * unkillable or have been already oom reaped or the are in
- * the middle of vfork
+ * Do not consider tasks which have already been oom reaped or
+ * that are in the middle of vfork.
*/
adj = (long)p->signal->oom_score_adj;
- if (adj == OOM_SCORE_ADJ_MIN ||
- test_bit(MMF_OOM_SKIP, &p->mm->flags) ||
- in_vfork(p)) {
+ if (test_bit(MMF_OOM_SKIP, &p->mm->flags) || in_vfork(p)) {
task_unlock(p);
+ if (obp != NULL) {
+ obp->mempts = 0;
+ obp->adj = adj;
+ }
return 0;
}

@@ -228,6 +231,16 @@ unsigned long oom_badness(struct task_struct *p, unsigned long totalpages)
mm_pgtables_bytes(p->mm) / PAGE_SIZE;
task_unlock(p);

+ /* Also return raw mempts and oom_score_adj along */
+ if (obp != NULL) {
+ obp->mempts = points;
+ obp->adj = adj;
+ }
+
+ /* Unkillable oom task skipped but returns mempts and oom_score_adj */
+ if (adj == OOM_SCORE_ADJ_MIN)
+ return 0;
+
/* Normalize to oom_score_adj units */
adj *= totalpages / 1000;
points += adj;
@@ -310,6 +323,8 @@ static int oom_evaluate_task(struct task_struct *task, void *arg)
{
struct oom_control *oc = arg;
unsigned long points;
+ struct oom_bad_parms obp = { .memcg = NULL, .nodemask = oc->nodemask,
+ .mempts = 0, .adj = 0 };

if (oom_unkillable_task(task))
goto next;
@@ -339,7 +354,7 @@ static int oom_evaluate_task(struct task_struct *task, void *arg)
goto select;
}

- points = oom_badness(task, oc->totalpages);
+ points = oom_badness(task, oc->totalpages, &obp);
if (!points || points < oc->chosen_points)
goto next;

@@ -349,6 +364,8 @@ static int oom_evaluate_task(struct task_struct *task, void *arg)
get_task_struct(task);
oc->chosen = task;
oc->chosen_points = points;
+ oc->chosen_mempts = obp.mempts;
+ oc->chosen_adj = obp.adj;
next:
return 0;
abort:
@@ -375,6 +392,9 @@ static void select_bad_process(struct oom_control *oc)
break;
rcu_read_unlock();
}
+
+ oc->chosen_points = oc->chosen_points * 1000 / oc->totalpages;
+ oc->chosen_mempts = oc->chosen_mempts * 1000 / oc->totalpages;
}

static int dump_task(struct task_struct *p, void *arg)
@@ -853,7 +873,8 @@ static bool task_will_free_mem(struct task_struct *task)
return ret;
}

-static void __oom_kill_process(struct task_struct *victim, const char *message)
+static void __oom_kill_process(struct task_struct *victim, const char *message,
+ struct oom_control *oc)
{
struct task_struct *p;
struct mm_struct *mm;
@@ -884,12 +905,24 @@ static void __oom_kill_process(struct task_struct *victim, const char *message)
*/
do_send_sig_info(SIGKILL, SEND_SIG_PRIV, victim, PIDTYPE_TGID);
mark_oom_victim(victim);
- pr_err("%s: Killed process %d (%s) total-vm:%lukB, anon-rss:%lukB, file-rss:%lukB, shmem-rss:%lukB\n",
- message, task_pid_nr(victim), victim->comm,
- K(victim->mm->total_vm),
- K(get_mm_counter(victim->mm, MM_ANONPAGES)),
- K(get_mm_counter(victim->mm, MM_FILEPAGES)),
- K(get_mm_counter(victim->mm, MM_SHMEMPAGES)));
+
+ if (oc != NULL && oc->chosen_mempts > 0)
+ pr_info("%s: Killed process %d (%s) total-vm:%lukB, anon-rss:%lukB, file-rss:%lukB, shmem-rss:%lukB, memory-usage:%lu.%1lu%% oom_score:%lu oom_score_adj:%ld total-pages: %lukB",
+ message, task_pid_nr(victim), victim->comm,
+ K(victim->mm->total_vm),
+ K(get_mm_counter(victim->mm, MM_ANONPAGES)),
+ K(get_mm_counter(victim->mm, MM_FILEPAGES)),
+ K(get_mm_counter(victim->mm, MM_SHMEMPAGES)),
+ oc->chosen_mempts / 10, oc->chosen_mempts % 10,
+ oc->chosen_points, oc->chosen_adj, K(oc->totalpages));
+ else
+ pr_info("%s: Killed process %d (%s) total-vm:%lukB, anon-rss:%lukB, file-rss:%lukB, shmem-rss:%lukB",
+ message, task_pid_nr(victim), victim->comm,
+ K(victim->mm->total_vm),
+ K(get_mm_counter(victim->mm, MM_ANONPAGES)),
+ K(get_mm_counter(victim->mm, MM_FILEPAGES)),
+ K(get_mm_counter(victim->mm, MM_SHMEMPAGES)));
+
task_unlock(victim);

/*
@@ -942,7 +975,7 @@ static int oom_kill_memcg_member(struct task_struct *task, void *message)
if (task->signal->oom_score_adj != OOM_SCORE_ADJ_MIN &&
!is_global_init(task)) {
get_task_struct(task);
- __oom_kill_process(task, message);
+ __oom_kill_process(task, message, NULL);
}
return 0;
}
@@ -979,7 +1012,7 @@ static void oom_kill_process(struct oom_control *oc, const char *message)
*/
oom_group = mem_cgroup_get_oom_group(victim, oc->memcg);

- __oom_kill_process(victim, message);
+ __oom_kill_process(victim, message, oc);

/*
* If necessary, kill all tasks in the selected memory cgroup.
--
2.20.1