Currently, we won't select a new oom victim until the previous one has passed away. This might lead to a deadlock if an allocating task holds a lock needed by the victim to complete. To cope with this problem, this patch introduced oom timeout, after which a new task will be selected even if the previous victim hasn't died. The timeout can be configured by sysctl vm.oom_timeout. It equals 5 seconds by default.
https://jira.sw.ru/browse/PSBM-38581 Signed-off-by: Vladimir Davydov <[email protected]> --- include/linux/oom.h | 3 +++ include/linux/sched.h | 2 ++ kernel/sysctl.c | 7 +++++++ mm/memcontrol.c | 2 +- mm/oom_kill.c | 26 +++++++++++++++++++++----- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/include/linux/oom.h b/include/linux/oom.h index 201aa1ab1dd2..f16e35bee7d9 100644 --- a/include/linux/oom.h +++ b/include/linux/oom.h @@ -52,6 +52,8 @@ static inline bool oom_task_origin(const struct task_struct *p) /* linux/mm/oom_group.c */ extern int get_task_oom_score_adj(struct task_struct *t); +extern void set_tsk_memdie(struct task_struct *p); + extern unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg, const nodemask_t *nodemask, unsigned long totalpages); extern void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, @@ -92,4 +94,5 @@ extern struct task_struct *find_lock_task_mm(struct task_struct *p); extern int sysctl_oom_dump_tasks; extern int sysctl_oom_kill_allocating_task; extern int sysctl_panic_on_oom; +extern int sysctl_oom_timeout; #endif /* _INCLUDE_LINUX_OOM_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 12dc066e0681..ac5f9f2ba4b3 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1582,6 +1582,8 @@ struct task_struct { unsigned int sequential_io_avg; #endif + unsigned long memdie_start; + /* reserved for Red Hat */ #ifdef CONFIG_DETECT_HUNG_TASK RH_KABI_USE(1, unsigned long last_switch_count) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8478a1e762d5..5a3ff6cb15fc 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1184,6 +1184,13 @@ static struct ctl_table vm_table[] = { .proc_handler = proc_dointvec, }, { + .procname = "oom_timeout", + .data = &sysctl_oom_timeout, + .maxlen = sizeof(sysctl_oom_timeout), + .mode = 0644, + .proc_handler = proc_dointvec_ms_jiffies, + }, + { .procname = "overcommit_ratio", .data = &sysctl_overcommit_ratio, .maxlen = sizeof(sysctl_overcommit_ratio), diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 47d859c6cb6f..b27b4148600b 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1916,7 +1916,7 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, * quickly exit and free its memory. */ if (fatal_signal_pending(current) || current->flags & PF_EXITING) { - set_thread_flag(TIF_MEMDIE); + set_tsk_memdie(current); return; } diff --git a/mm/oom_kill.c b/mm/oom_kill.c index ca765a82fa1a..0d569020390a 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -42,6 +42,7 @@ int sysctl_panic_on_oom; int sysctl_oom_kill_allocating_task; int sysctl_oom_dump_tasks = 1; +int sysctl_oom_timeout = 5 * HZ; static DEFINE_SPINLOCK(zone_scan_lock); #ifdef CONFIG_NUMA @@ -270,6 +271,10 @@ enum oom_scan_t oom_scan_process_thread(struct task_struct *task, if (test_tsk_thread_flag(task, TIF_MEMDIE)) { if (unlikely(frozen(task))) __thaw_task(task); + smp_rmb(); /* matches smp_wmb() in set_tsk_memdie() */ + if (time_after(jiffies, task->memdie_start + + sysctl_oom_timeout)) + return OOM_SCAN_CONTINUE; if (!force_kill) return OOM_SCAN_ABORT; } @@ -283,13 +288,13 @@ enum oom_scan_t oom_scan_process_thread(struct task_struct *task, if (oom_task_origin(task)) return OOM_SCAN_SELECT; - if (task->flags & PF_EXITING && !force_kill) { + if (task->flags & PF_EXITING) { /* * If this task is not being ptraced on exit, then wait for it * to finish before killing some other task unnecessarily. */ if (!(task->group_leader->ptrace & PT_TRACE_EXIT)) - return OOM_SCAN_ABORT; + return OOM_SCAN_SELECT; } return OOM_SCAN_OK; } @@ -412,6 +417,15 @@ static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order, dump_tasks(memcg, nodemask); } +void set_tsk_memdie(struct task_struct *p) +{ + if (!test_tsk_thread_flag(p, TIF_MEMDIE)) { + p->memdie_start = jiffies; + smp_wmb(); /* matches smp_rmb() in oom_scan_process_thread() */ + set_tsk_thread_flag(p, TIF_MEMDIE); + } +} + #define K(x) ((x) << (PAGE_SHIFT-10)) /* * Must be called while holding a reference to p, which will be released upon @@ -435,7 +449,7 @@ void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, * its children or threads, just set TIF_MEMDIE so it can die quickly */ if (p->flags & PF_EXITING) { - set_tsk_thread_flag(p, TIF_MEMDIE); + set_tsk_memdie(p); goto out; } @@ -460,6 +474,8 @@ void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, if (child->mm == p->mm) continue; + if (test_tsk_thread_flag(child, TIF_MEMDIE)) + continue; /* * oom_badness() returns 0 if the thread is unkillable */ @@ -518,7 +534,7 @@ void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, } rcu_read_unlock(); - set_tsk_thread_flag(victim, TIF_MEMDIE); + set_tsk_memdie(victim); do_send_sig_info(SIGKILL, SEND_SIG_FORCED, victim, true); mem_cgroup_note_oom_kill(memcg, victim); out: @@ -649,7 +665,7 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, * quickly exit and free its memory. */ if (fatal_signal_pending(current) || current->flags & PF_EXITING) { - set_thread_flag(TIF_MEMDIE); + set_tsk_memdie(current); boost_dying_task(current); return; } -- 2.1.4 _______________________________________________ Devel mailing list [email protected] https://lists.openvz.org/mailman/listinfo/devel
