cregit-Linux how code gets into the kernel

Release 4.7 mm/oom_kill.c

Directory: mm
/*
 *  linux/mm/oom_kill.c
 * 
 *  Copyright (C)  1998,2000  Rik van Riel
 *      Thanks go out to Claus Fischer for some serious inspiration and
 *      for goading me into coding this file...
 *  Copyright (C)  2010  Google, Inc.
 *      Rewritten by David Rientjes
 *
 *  The routines in this file are used to kill a process when
 *  we're seriously out of memory. This gets called from __alloc_pages()
 *  in mm/page_alloc.c when we really run out of memory.
 *
 *  Since we won't call these routines often (on a well-configured
 *  machine) this file will double as a 'coding guide' and a signpost
 *  for newbie kernel hackers. It features several pointers to major
 *  kernel subsystems and hints as to where to find out what things do.
 */

#include <linux/oom.h>
#include <linux/mm.h>
#include <linux/err.h>
#include <linux/gfp.h>
#include <linux/sched.h>
#include <linux/swap.h>
#include <linux/timex.h>
#include <linux/jiffies.h>
#include <linux/cpuset.h>
#include <linux/export.h>
#include <linux/notifier.h>
#include <linux/memcontrol.h>
#include <linux/mempolicy.h>
#include <linux/security.h>
#include <linux/ptrace.h>
#include <linux/freezer.h>
#include <linux/ftrace.h>
#include <linux/ratelimit.h>
#include <linux/kthread.h>
#include <linux/init.h>

#include <asm/tlb.h>
#include "internal.h"


#define CREATE_TRACE_POINTS
#include <trace/events/oom.h>


int sysctl_panic_on_oom;

int sysctl_oom_kill_allocating_task;

int sysctl_oom_dump_tasks = 1;


DEFINE_MUTEX(oom_lock);

#ifdef CONFIG_NUMA
/**
 * has_intersects_mems_allowed() - check task eligiblity for kill
 * @start: task struct of which task to consider
 * @mask: nodemask passed to page allocator for mempolicy ooms
 *
 * Task eligibility is determined by whether or not a candidate task, @tsk,
 * shares the same mempolicy nodes as current if it is bound by such a policy
 * and whether or not it has the same set of allowed cpuset nodes.
 */

static bool has_intersects_mems_allowed(struct task_struct *start, const nodemask_t *mask) { struct task_struct *tsk; bool ret = false; rcu_read_lock(); for_each_thread(start, tsk) { if (mask) { /* * If this is a mempolicy constrained oom, tsk's * cpuset is irrelevant. Only return true if its * mempolicy intersects current, otherwise it may be * needlessly killed. */ ret = mempolicy_nodemask_intersects(tsk, mask); } else { /* * This is not a mempolicy constrained oom, so only * check the mems of tsk's cpuset. */ ret = cpuset_mems_allowed_intersects(current, tsk); } if (ret) break; } rcu_read_unlock(); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
oleg nesterovoleg nesterov3140.26%250.00%
david rientjesdavid rientjes2431.17%125.00%
kosaki motohirokosaki motohiro2228.57%125.00%
Total77100.00%4100.00%

#else
static bool has_intersects_mems_allowed(struct task_struct *tsk, const nodemask_t *mask) { return true; }

Contributors

PersonTokensPropCommitsCommitProp
david rientjesdavid rientjes1684.21%133.33%
kosaki motohirokosaki motohiro210.53%133.33%
oleg nesterovoleg nesterov15.26%133.33%
Total19100.00%3100.00%

#endif /* CONFIG_NUMA */ /* * The process p may have detached its own ->mm while exiting or through * use_mm(), but one or more of its subthreads may still have a valid * pointer. Return p, or any of its subthreads with a valid ->mm, with * task_lock() held. */
struct task_struct *find_lock_task_mm(struct task_struct *p) { struct task_struct *t; rcu_read_lock(); for_each_thread(p, t) { task_lock(t); if (likely(t->mm)) goto found; task_unlock(t); } t = NULL; found: rcu_read_unlock(); return t; }

Contributors

PersonTokensPropCommitsCommitProp
oleg nesterovoleg nesterov62100.00%3100.00%
Total62100.00%3100.00%

/* * order == -1 means the oom kill is required by sysrq, otherwise only * for display purposes. */
static inline bool is_sysrq_oom(struct oom_control *oc) { return oc->order == -1; }

Contributors

PersonTokensPropCommitsCommitProp
yaowei baiyaowei bai20100.00%1100.00%
Total20100.00%1100.00%

/* return true if the task is not adequate as candidate victim task. */
static bool oom_unkillable_task(struct task_struct *p, struct mem_cgroup *memcg, const nodemask_t *nodemask) { if (is_global_init(p)) return true; if (p->flags & PF_KTHREAD) return true; /* When mem_cgroup_out_of_memory() and p is not member of the group */ if (memcg && !task_in_mem_cgroup(p, memcg)) return true; /* p may not have freeable memory in nodemask */ if (!has_intersects_mems_allowed(p, nodemask)) return true; return false; }

Contributors

PersonTokensPropCommitsCommitProp
kosaki motohirokosaki motohiro7194.67%133.33%
johannes weinerjohannes weiner34.00%133.33%
oleg nesterovoleg nesterov11.33%133.33%
Total75100.00%3100.00%

/** * oom_badness - heuristic function to determine which candidate task to kill * @p: task struct of which task we should calculate * @totalpages: total present RAM allowed for page allocation * * The heuristic for determining which task to kill is made to be as simple and * 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, struct mem_cgroup *memcg, const nodemask_t *nodemask, unsigned long totalpages) { long points; long adj; if (oom_unkillable_task(p, memcg, nodemask)) return 0; p = find_lock_task_mm(p); if (!p) return 0; /* * Do not even consider tasks which are explicitly marked oom * unkillable or have been already oom reaped. */ adj = (long)p->signal->oom_score_adj; if (adj == OOM_SCORE_ADJ_MIN || test_bit(MMF_OOM_REAPED, &p->mm->flags)) { task_unlock(p); return 0; } /* * The baseline for the badness score is the proportion of RAM that each * task's rss, pagetable and swap space use. */ points = get_mm_rss(p->mm) + get_mm_counter(p->mm, MM_SWAPENTS) + atomic_long_read(&p->mm->nr_ptes) + mm_nr_pmds(p->mm); task_unlock(p); /* * Root processes get 3% bonus, just like the __vm_enough_memory() * implementation used by LSMs. */ if (has_capability_noaudit(p, CAP_SYS_ADMIN)) points -= (points * 3) / 100; /* Normalize to oom_score_adj units */ adj *= totalpages / 1000; points += adj; /* * Never return 0 for an eligible task regardless of the root bonus and * oom_score_adj (oom_score_adj can't be OOM_SCORE_ADJ_MIN here). */ return points > 0 ? points : 1; }

Contributors

PersonTokensPropCommitsCommitProp
david rientjesdavid rientjes5429.67%623.08%
michal hockomichal hocko3117.03%27.69%
kosaki motohirokosaki motohiro3117.03%519.23%
pre-gitpre-git3117.03%13.85%
kirill a. shutemovkirill a. shutemov147.69%27.69%
andrew mortonandrew morton63.30%27.69%
oleg nesterovoleg nesterov42.20%13.85%
tim schmielautim schmielau31.65%13.85%
johannes weinerjohannes weiner21.10%13.85%
ingo molnaringo molnar21.10%13.85%
cyrill gorcunovcyrill gorcunov10.55%13.85%
andrew g. morganandrew g. morgan10.55%13.85%
frantisek hrbatafrantisek hrbata10.55%13.85%
eric pariseric paris10.55%13.85%
Total182100.00%26100.00%

/* * Determine the type of allocation constraint. */ #ifdef CONFIG_NUMA
static enum oom_constraint constrained_alloc(struct oom_control *oc, unsigned long *totalpages) { struct zone *zone; struct zoneref *z; enum zone_type high_zoneidx = gfp_zone(oc->gfp_mask); bool cpuset_limited = false; int nid; /* Default to all available memory */ *totalpages = totalram_pages + total_swap_pages; if (!oc->zonelist) return CONSTRAINT_NONE; /* * Reach here only when __GFP_NOFAIL is used. So, we should avoid * to kill current.We have to random task kill in this case. * Hopefully, CONSTRAINT_THISNODE...but no way to handle it, now. */ if (oc->gfp_mask & __GFP_THISNODE) return CONSTRAINT_NONE; /* * This is not a __GFP_THISNODE allocation, so a truncated nodemask in * the page allocator means a mempolicy is in effect. Cpuset policy * is enforced in get_page_from_freelist(). */ if (oc->nodemask && !nodes_subset(node_states[N_MEMORY], *oc->nodemask)) { *totalpages = total_swap_pages; for_each_node_mask(nid, *oc->nodemask) *totalpages += node_spanned_pages(nid); return CONSTRAINT_MEMORY_POLICY; } /* Check this allocation failure is caused by cpuset's wall function */ for_each_zone_zonelist_nodemask(zone, z, oc->zonelist, high_zoneidx, oc->nodemask) if (!cpuset_zone_allowed(zone, oc->gfp_mask)) cpuset_limited = true; if (cpuset_limited) { *totalpages = total_swap_pages; for_each_node_mask(nid, cpuset_current_mems_allowed) *totalpages += node_spanned_pages(nid); return CONSTRAINT_CPUSET; } return CONSTRAINT_NONE; }

Contributors

PersonTokensPropCommitsCommitProp
david rientjesdavid rientjes10255.43%330.00%
kamezawa hiroyukikamezawa hiroyuki3116.85%110.00%
christoph lameterchristoph lameter2714.67%220.00%
mel gormanmel gorman2211.96%220.00%
vladimir davydovvladimir davydov10.54%110.00%
lai jiangshanlai jiangshan10.54%110.00%
Total184100.00%10100.00%

#else
static enum oom_constraint constrained_alloc(struct oom_control *oc, unsigned long *totalpages) { *totalpages = totalram_pages + total_swap_pages; return CONSTRAINT_NONE; }

Contributors

PersonTokensPropCommitsCommitProp
david rientjesdavid rientjes1451.85%250.00%
kamezawa hiroyukikamezawa hiroyuki933.33%125.00%
christoph lameterchristoph lameter414.81%125.00%
Total27100.00%4100.00%

#endif
enum oom_scan_t oom_scan_process_thread(struct oom_control *oc, struct task_struct *task, unsigned long totalpages) { if (oom_unkillable_task(task, NULL, oc->nodemask)) return OOM_SCAN_CONTINUE; /* * This task already has access to memory reserves and is being killed. * Don't allow any other task to have access to the reserves. */ if (!is_sysrq_oom(oc) && atomic_read(&task->signal->oom_victims)) return OOM_SCAN_ABORT; /* * If task is allocating a lot of memory and has been marked to be * killed first if it triggers an oom, then select it. */ if (oom_task_origin(task)) return OOM_SCAN_SELECT; return OOM_SCAN_OK; }

Contributors

PersonTokensPropCommitsCommitProp
david rientjesdavid rientjes4765.28%956.25%
tetsuo handatetsuo handa912.50%16.25%
pre-gitpre-git56.94%16.25%
yaowei baiyaowei bai45.56%16.25%
oleg nesterovoleg nesterov22.78%16.25%
kurt garloffkurt garloff22.78%16.25%
nick pigginnick piggin22.78%16.25%
kosaki motohirokosaki motohiro11.39%16.25%
Total72100.00%16100.00%

/* * Simple selection loop. We chose the process with the highest * number of 'points'. Returns -1 on scan abort. */
static struct task_struct *select_bad_process(struct oom_control *oc, unsigned int *ppoints, unsigned long totalpages) { struct task_struct *p; struct task_struct *chosen = NULL; unsigned long chosen_points = 0; rcu_read_lock(); for_each_process(p) { unsigned int points; switch (oom_scan_process_thread(oc, p, totalpages)) { case OOM_SCAN_SELECT: chosen = p; chosen_points = ULONG_MAX; /* fall through */ case OOM_SCAN_CONTINUE: continue; case OOM_SCAN_ABORT: rcu_read_unlock(); return (struct task_struct *)(-1UL); case OOM_SCAN_OK: break; }; points = oom_badness(p, NULL, oc->nodemask, totalpages); if (!points || points < chosen_points) continue; chosen = p; chosen_points = points; } if (chosen) get_task_struct(chosen); rcu_read_unlock(); *ppoints = chosen_points * 1000 / totalpages; return chosen; }

Contributors

PersonTokensPropCommitsCommitProp
david rientjesdavid rientjes12778.88%857.14%
pre-gitpre-git1911.80%17.14%
rusty russellrusty russell74.35%17.14%
kosaki motohirokosaki motohiro31.86%17.14%
andrea arcangeliandrea arcangeli31.86%17.14%
tetsuo handatetsuo handa10.62%17.14%
tim schmielautim schmielau10.62%17.14%
Total161100.00%14100.00%

/** * dump_tasks - dump current memory state of all system tasks * @memcg: current's memory controller, if constrained * @nodemask: nodemask passed to page allocator for mempolicy ooms * * Dumps the current memory state of all eligible tasks. Tasks not in the same * memcg, not in the same cpuset, or bound to a disjoint set of mempolicy nodes * are not shown. * State information includes task's pid, uid, tgid, vm size, rss, nr_ptes, * swapents, oom_score_adj value, and name. */
static void dump_tasks(struct mem_cgroup *memcg, const nodemask_t *nodemask) { struct task_struct *p; struct task_struct *task; pr_info("[ pid ] uid tgid total_vm rss nr_ptes nr_pmds swapents oom_score_adj name\n"); rcu_read_lock(); for_each_process(p) { if (oom_unkillable_task(p, memcg, nodemask)) continue; task = find_lock_task_mm(p); if (!task) { /* * This is a kthread or all of p's threads have already * detached their mm's. There's no need to report * them; they can't be oom killed anyway. */ continue; } pr_info("[%5d] %5d %5d %8lu %8lu %7ld %7ld %8lu %5hd %s\n", task->pid, from_kuid(&init_user_ns, task_uid(task)), task->tgid, task->mm->total_vm, get_mm_rss(task->mm), atomic_long_read(&task->mm->nr_ptes), mm_nr_pmds(task->mm), get_mm_counter(task->mm, MM_SWAPENTS), task->signal->oom_score_adj, task->comm); task_unlock(task); } rcu_read_unlock(); }

Contributors

PersonTokensPropCommitsCommitProp
david rientjesdavid rientjes10469.80%956.25%
kosaki motohirokosaki motohiro2214.77%212.50%
kirill a. shutemovkirill a. shutemov138.72%212.50%
eric w. biedermaneric w. biederman64.03%16.25%
johannes weinerjohannes weiner21.34%16.25%
david howellsdavid howells21.34%16.25%
Total149100.00%16100.00%


static void dump_header(struct oom_control *oc, struct task_struct *p, struct mem_cgroup *memcg) { pr_warn("%s invoked oom-killer: gfp_mask=%#x(%pGg), order=%d, oom_score_adj=%hd\n", current->comm, oc->gfp_mask, &oc->gfp_mask, oc->order, current->signal->oom_score_adj); cpuset_print_current_mems_allowed(); dump_stack(); if (memcg) mem_cgroup_print_oom_info(memcg, p); else show_mem(SHOW_MEM_FILTER_NODES); if (sysctl_oom_dump_tasks) dump_tasks(memcg, oc->nodemask); }

Contributors

PersonTokensPropCommitsCommitProp
david rientjesdavid rientjes6475.29%758.33%
vlastimil babkavlastimil babka67.06%18.33%
daisuke nishimuradaisuke nishimura67.06%18.33%
sha zhengjusha zhengju55.88%18.33%
johannes weinerjohannes weiner33.53%18.33%
joe perchesjoe perches11.18%18.33%
Total85100.00%12100.00%

/* * Number of OOM victims in flight */ static atomic_t oom_victims = ATOMIC_INIT(0); static DECLARE_WAIT_QUEUE_HEAD(oom_victims_wait); bool oom_killer_disabled __read_mostly; #define K(x) ((x) << (PAGE_SHIFT-10)) /* * task->mm can be NULL if the task is the exited group leader. So to * determine whether the task is using a particular mm, we examine all the * task's threads: if one of those is using this mm then this task was also * using it. */
static bool process_shares_mm(struct task_struct *p, struct mm_struct *mm) { struct task_struct *t; for_each_thread(p, t) { struct mm_struct *t_mm = READ_ONCE(t->mm); if (t_mm) return t_mm == mm; } return false; }

Contributors

PersonTokensPropCommitsCommitProp
michal hockomichal hocko53100.00%1100.00%
Total53100.00%1100.00%

#ifdef CONFIG_MMU /* * OOM Reaper kernel thread which tries to reap the memory used by the OOM * victim (if that is possible) to help the OOM killer to move on. */ static struct task_struct *oom_reaper_th; static DECLARE_WAIT_QUEUE_HEAD(oom_reaper_wait); static struct task_struct *oom_reaper_list; static DEFINE_SPINLOCK(oom_reaper_lock);
static bool __oom_reap_task(struct task_struct *tsk) { struct mmu_gather tlb; struct vm_area_struct *vma; struct mm_struct *mm = NULL; struct task_struct *p; struct zap_details details = {.check_swap_entries = true, .ignore_dirty = true}; bool ret = true; /* * We have to make sure to not race with the victim exit path * and cause premature new oom victim selection: * __oom_reap_task exit_mm * atomic_inc_not_zero * mmput * atomic_dec_and_test * exit_oom_victim * [...] * out_of_memory * select_bad_process * # no TIF_MEMDIE task selects new victim * unmap_page_range # frees some memory */ mutex_lock(&oom_lock); /* * Make sure we find the associated mm_struct even when the particular * thread has already terminated and cleared its mm. * We might have race with exit path so consider our work done if there * is no mm. */ p = find_lock_task_mm(tsk); if (!p) goto unlock_oom; mm = p->mm; atomic_inc(&mm->mm_users); task_unlock(p); if (!down_read_trylock(&mm->mmap_sem)) { ret = false; goto unlock_oom; } tlb_gather_mmu(&tlb, mm, 0, -1); for (vma = mm->mmap ; vma; vma = vma->vm_next) { if (is_vm_hugetlb_page(vma)) continue; /* * mlocked VMAs require explicit munlocking before unmap. * Let's keep it simple here and skip such VMAs. */ if (vma->vm_flags & VM_LOCKED) continue; /* * Only anonymous pages have a good chance to be dropped * without additional steps which we cannot afford as we * are OOM already. * * We do not even care about fs backed pages because all * which are reclaimable have already been reclaimed and * we do not want to block exit_mmap by keeping mm ref * count elevated without a good reason. */ if (vma_is_anonymous(vma) || !(vma->vm_flags & VM_SHARED)) unmap_page_range(&tlb, vma, vma->vm_start, vma->vm_end, &details); } tlb_finish_mmu(&tlb, 0, -1); pr_info("oom_reaper: reaped process %d (%s), now anon-rss:%lukB, file-rss:%lukB, shmem-rss:%lukB\n", task_pid_nr(tsk), tsk->comm, K(get_mm_counter(mm, MM_ANONPAGES)), K(get_mm_counter(mm, MM_FILEPAGES)), K(get_mm_counter(mm, MM_SHMEMPAGES))); up_read(&mm->mmap_sem); /* * This task can be safely ignored because we cannot do much more * to release its memory. */ set_bit(MMF_OOM_REAPED, &mm->flags); unlock_oom: mutex_unlock(&oom_lock); /* * Drop our reference but make sure the mmput slow path is called from a * different context because we shouldn't risk we get stuck there and * put the oom_reaper out of the way. */ if (mm) mmput_async(mm); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
michal hockomichal hocko29499.66%787.50%
tetsuo handatetsuo handa10.34%112.50%
Total295100.00%8100.00%

#define MAX_OOM_REAP_RETRIES 10
static void oom_reap_task(struct task_struct *tsk) { int attempts = 0; /* Retry the down_read_trylock(mmap_sem) a few times */ while (attempts++ < MAX_OOM_REAP_RETRIES && !__oom_reap_task(tsk)) schedule_timeout_idle(HZ/10); if (attempts > MAX_OOM_REAP_RETRIES) { pr_info("oom_reaper: unable to reap pid:%d (%s)\n", task_pid_nr(tsk), tsk->comm); debug_show_all_locks(); } /* * Clear TIF_MEMDIE because the task shouldn't be sitting on a * reasonably reclaimable memory anymore or it is not a good candidate * for the oom victim right now because it cannot release its memory * itself nor by the oom reaper. */ tsk->oom_reaper_list = NULL; exit_oom_victim(tsk); /* Drop a reference taken by wake_oom_reaper */ put_task_struct(tsk); }

Contributors

PersonTokensPropCommitsCommitProp
michal hockomichal hocko80100.00%4100.00%
Total80100.00%4100.00%


static int oom_reaper(void *unused) { set_freezable(); while (true) { struct task_struct *tsk = NULL; wait_event_freezable(oom_reaper_wait, oom_reaper_list != NULL); spin_lock(&oom_reaper_lock); if (oom_reaper_list != NULL) { tsk = oom_reaper_list; oom_reaper_list = tsk->oom_reaper_list; } spin_unlock(&oom_reaper_lock); if (tsk) oom_reap_task(tsk); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
michal hockomichal hocko7192.21%480.00%
vladimir davydovvladimir davydov67.79%120.00%
Total77100.00%5100.00%


static void wake_oom_reaper(struct task_struct *tsk) { if (!oom_reaper_th) return; /* tsk is already queued? */ if (tsk == oom_reaper_list || tsk->oom_reaper_list) return; get_task_struct(tsk); spin_lock(&oom_reaper_lock); tsk->oom_reaper_list = oom_reaper_list; oom_reaper_list = tsk; spin_unlock(&oom_reaper_lock); wake_up(&oom_reaper_wait); }

Contributors

PersonTokensPropCommitsCommitProp
michal hockomichal hocko5385.48%466.67%
vladimir davydovvladimir davydov58.06%116.67%
tetsuo handatetsuo handa46.45%116.67%
Total62100.00%6100.00%

/* Check if we can reap the given task. This has to be called with stable * tsk->mm */
void try_oom_reaper(struct task_struct *tsk) { struct mm_struct *mm = tsk->mm; struct task_struct *p; if (!mm) return; /* * There might be other threads/processes which are either not * dying or even not killable. */ if (atomic_read(&mm->mm_users) > 1) { rcu_read_lock(); for_each_process(p) { if (!process_shares_mm(p, mm)) continue; if (fatal_signal_pending(p)) continue; /* * If the task is exiting make sure the whole thread group * is exiting and cannot acces mm anymore. */ if (signal_group_exit(p->signal)) continue; /* Give up */ rcu_read_unlock(); return; } rcu_read_unlock(); } wake_oom_reaper(tsk); }

Contributors

PersonTokensPropCommitsCommitProp
michal hockomichal hocko97100.00%2100.00%
Total97100.00%2100.00%


static int __init oom_init(void) { oom_reaper_th = kthread_run(oom_reaper, NULL, "oom_reaper"); if (IS_ERR(oom_reaper_th)) { pr_err("Unable to start OOM reaper %ld. Continuing regardless\n", PTR_ERR(oom_reaper_th)); oom_reaper_th = NULL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
michal hockomichal hocko46100.00%1100.00%
Total46100.00%1100.00%

subsys_initcall(oom_init) #else
static void wake_oom_reaper(struct task_struct *tsk) { }

Contributors

PersonTokensPropCommitsCommitProp
michal hockomichal hocko10100.00%2100.00%
Total10100.00%2100.00%

#endif /** * mark_oom_victim - mark the given task as OOM victim * @tsk: task to mark * * Has to be called with oom_lock held and never after * oom has been disabled already. */
void mark_oom_victim(struct task_struct *tsk) { WARN_ON(oom_killer_disabled); /* OOM killer might race with memcg OOM */ if (test_and_set_tsk_thread_flag(tsk, TIF_MEMDIE)) return; atomic_inc(&tsk->signal->oom_victims); /* * Make sure that the task is woken up from uninterruptible sleep * if it is frozen because OOM killer wouldn't be able to free * any memory and livelock. freezing_slow_path will tell the freezer * that TIF_MEMDIE tasks should be ignored. */ __thaw_task(tsk); atomic_inc(&oom_victims); }

Contributors

PersonTokensPropCommitsCommitProp
michal hockomichal hocko3777.08%250.00%
tetsuo handatetsuo handa1020.83%125.00%
johannes weinerjohannes weiner12.08%125.00%
Total48100.00%4100.00%

/** * exit_oom_victim - note the exit of an OOM victim */
void exit_oom_victim(struct task_struct *tsk) { if (!test_and_clear_tsk_thread_flag(tsk, TIF_MEMDIE)) return; atomic_dec(&tsk->signal->oom_victims); if (!atomic_dec_return(&oom_victims)) wake_up_all(&oom_victims_wait); }

Contributors

PersonTokensPropCommitsCommitProp
michal hockomichal hocko3576.09%360.00%
tetsuo handatetsuo handa1021.74%120.00%
johannes weinerjohannes weiner12.17%120.00%
Total46100.00%5100.00%

/** * oom_killer_disable - disable OOM killer * * Forces all page allocations to fail rather than trigger OOM killer. * Will block and wait until all OOM victims are killed. * * The function cannot be called when there are runnable user tasks because * the userspace would see unexpected allocation failures as a result. Any * new usage of this function should be consulted with MM people. * * Returns true if successful and false if the OOM killer cannot be * disabled. */
bool oom_killer_disable(void) { /* * Make sure to not race with an ongoing OOM killer. Check that the * current is not killed (possibly due to sharing the victim's memory). */ if (mutex_lock_killable(&oom_lock)) return false; oom_killer_disabled = true; mutex_unlock(&oom_lock); wait_event(oom_victims_wait, !atomic_read(&oom_victims)); return true; }

Contributors

PersonTokensPropCommitsCommitProp
michal hockomichal hocko3886.36%360.00%
johannes weinerjohannes weiner36.82%120.00%
tetsuo handatetsuo handa36.82%120.00%
Total44100.00%5100.00%

/** * oom_killer_enable - enable OOM killer */
void oom_killer_enable(void) { oom_killer_disabled = false; }

Contributors

PersonTokensPropCommitsCommitProp
michal hockomichal hocko11100.00%2100.00%
Total11100.00%2100.00%

/* * Must be called while holding a reference to p, which will be released upon * returning. */
void oom_kill_process(struct oom_control *oc, struct task_struct *p, unsigned int points, unsigned long totalpages, struct mem_cgroup *memcg, const char *message) { struct task_struct *victim = p; struct task_struct *child; struct task_struct *t; struct mm_struct *mm; unsigned int victim_points = 0; static DEFINE_RATELIMIT_STATE(oom_rs, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); bool can_oom_reap = true; /* * If the task is already exiting, don't alarm the sysadmin or kill * its children or threads, just set TIF_MEMDIE so it can die quickly */ task_lock(p); if (p->mm && task_will_free_mem(p)) { mark_oom_victim(p); try_oom_reaper(p); task_unlock(p); put_task_struct(p); return; } task_unlock(p); if (__ratelimit(&oom_rs)) dump_header(oc, p, memcg); pr_err("%s: Kill process %d (%s) score %u or sacrifice child\n", message, task_pid_nr(p), p->comm, points); /* * If any of p's children has a different mm and is eligible for kill, * the one with the highest oom_badness() score is sacrificed for its * parent. This attempts to lose the minimal amount of work done while * still freeing memory. */ read_lock(&tasklist_lock); for_each_thread(p, t) { list_for_each_entry(child, &t->children, sibling) { unsigned int child_points; if (process_shares_mm(child, p->mm)) continue; /* * oom_badness() returns 0 if the thread is unkillable */ child_points = oom_badness(child, memcg, oc->nodemask, totalpages); if (child_points > victim_points) { put_task_struct(victim); victim = child; victim_points = child_points; get_task_struct(victim); } } } read_unlock(&tasklist_lock); p = find_lock_task_mm(victim); if (!p) { put_task_struct(victim); return; } else if (victim != p) { get_task_struct(p); put_task_struct(victim); victim = p; } /* Get a reference to safely compare mm after task_unlock(victim) */ mm = victim->mm; atomic_inc(&mm->mm_count); /* * We should send SIGKILL before setting TIF_MEMDIE in order to prevent * the OOM victim from depleting the memory reserves from the user * space under its control. */ do_send_sig_info(SIGKILL, SEND_SIG_FORCED, victim, true); mark_oom_victim(victim); pr_err("Killed process %d (%s) total-vm:%lukB, anon-rss:%lukB, file-rss:%lukB, shmem-rss:%lukB\n", 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); /* * Kill all user processes sharing victim->mm in other thread groups, if * any. They don't get access to memory reserves, though, to avoid * depletion of all memory. This prevents mm->mmap_sem livelock when an * oom killed thread cannot exit because it requires the semaphore and * its contended by another thread trying to allocate memory itself. * That thread will now get access to memory reserves since it has a * pending fatal signal. */ rcu_read_lock(); for_each_process(p) { if (!process_shares_mm(p, mm)) continue; if (same_thread_group(p, victim)) continue; if (unlikely(p->flags & PF_KTHREAD) || is_global_init(p) || p->signal->oom_score_adj == OOM_SCORE_ADJ_MIN) { /* * We cannot use oom_reaper for the mm shared by this * process because it wouldn't get killed and so the * memory might be still used. */ can_oom_reap = false; continue; } do_send_sig_info(SIGKILL, SEND_SIG_FORCED, p, true); } rcu_read_unlock(); if (can_oom_reap) wake_oom_reaper(victim); mmdrop(mm); put_task_struct(victim); }

Contributors

PersonTokensPropCommitsCommitProp
david rientjesdavid rientjes26855.83%1126.19%
michal hockomichal hocko4910.21%49.52%
oleg nesterovoleg nesterov479.79%716.67%
tetsuo handatetsuo handa285.83%37.14%
andrea arcangeliandrea arcangeli193.96%12.38%
jerome marchandjerome marchand132.71%12.38%
kurt garloffkurt garloff132.71%12.38%
nick pigginnick piggin122.50%24.76%
linus torvaldslinus torvalds61.25%24.76%
christoph lameterchristoph lameter51.04%12.38%
kosaki motohirokosaki motohiro40.83%24.76%
johannes weinerjohannes weiner40.83%24.76%
chen jiechen jie40.83%12.38%
matthias kaehlckematthias kaehlcke30.62%12.38%
pavel emelianovpavel emelianov30.62%12.38%
wang longwang long10.21%12.38%
andrew mortonandrew morton10.21%12.38%
Total480100.00%42100.00%

#undef K /* * Determines whether the kernel must panic because of the panic_on_oom sysctl. */
void check_panic_on_oom(struct oom_control *oc, enum oom_constraint constraint, struct mem_cgroup *memcg) { if (likely(!sysctl_panic_on_oom)) return; if (sysctl_panic_on_oom != 2) { /* * panic_on_oom == 1 only affects CONSTRAINT_NONE, the kernel * does not panic for cpuset, mempolicy, or memcg allocation * failures. */ if (constraint != CONSTRAINT_NONE) return; } /* Do not panic for oom kills triggered by sysrq */ if (is_sysrq_oom(oc)) return; dump_header(oc, NULL, memcg); panic("Out of memory: %s panic_on_oom is enabled\n", sysctl_panic_on_oom == 2 ? "compulsory" : "system-wide"); }

Contributors

PersonTokensPropCommitsCommitProp
david rientjesdavid rientjes6080.00%350.00%
oleg nesterovoleg nesterov79.33%116.67%
balasubramani vivekanandanbalasubramani vivekanandan56.67%116.67%
yaowei baiyaowei bai34.00%116.67%
Total75100.00%6100.00%

static BLOCKING_NOTIFIER_HEAD(oom_notify_list);
int register_oom_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&oom_notify_list, nb); }

Contributors

PersonTokensPropCommitsCommitProp
martin schwidefskymartin schwidefsky19100.00%1100.00%
Total19100.00%1100.00%

EXPORT_SYMBOL_GPL(register_oom_notifier);
int unregister_oom_notifier(struct notifier_block *nb) { return blocking_notifier_chain_unregister(&oom_notify_list, nb); }

Contributors

PersonTokensPropCommitsCommitProp
martin schwidefskymartin schwidefsky19100.00%1100.00%
Total19100.00%1100.00%

EXPORT_SYMBOL_GPL(unregister_oom_notifier); /** * out_of_memory - kill the "best" process when we run out of memory * @oc: pointer to struct oom_control * * If we run out of memory, we have the choice between either * killing a random task (bad), letting the system crash (worse) * OR try to be smart about which process to kill. Note that we * don't have to be perfect here, we just have to be good. */
bool out_of_memory(struct oom_control *oc) { struct task_struct *p; unsigned long totalpages; unsigned long freed = 0; unsigned int uninitialized_var(points); enum oom_constraint constraint = CONSTRAINT_NONE; if (oom_killer_disabled) return false; blocking_notifier_call_chain(&oom_notify_list, 0, &freed); if (freed > 0) /* Got some memory back in the last second. */ return true; /* * If current has a pending SIGKILL or is exiting, then automatically * select it. The goal is to allow it to allocate so that it may * quickly exit and free its memory. * * But don't select if current has already released its mm and cleared * TIF_MEMDIE flag at exit_mm(), otherwise an OOM livelock may occur. */ if (current->mm && (fatal_signal_pending(current) || task_will_free_mem(current))) { mark_oom_victim(current); try_oom_reaper(current); return true; } /* * The OOM killer does not compensate for IO-less reclaim. * pagefault_out_of_memory lost its gfp context so we have to * make sure exclude 0 mask - all other users should have at least * ___GFP_DIRECT_RECLAIM to get here. */ if (oc->gfp_mask && !(oc->gfp_mask & (__GFP_FS|__GFP_NOFAIL))) return true; /* * Check if there were limitations on the allocation (only relevant for * NUMA) that may require different handling. */ constraint = constrained_alloc(oc, &totalpages); if (constraint != CONSTRAINT_MEMORY_POLICY) oc->nodemask = NULL; check_panic_on_oom(oc, constraint, NULL); if (sysctl_oom_kill_allocating_task && current->mm && !oom_unkillable_task(current, NULL, oc->nodemask) && current->signal->oom_score_adj != OOM_SCORE_ADJ_MIN) { get_task_struct(current); oom_kill_process(oc, current, 0, totalpages, NULL, "Out of memory (oom_kill_allocating_task)"); return true; } p = select_bad_process(oc, &points, totalpages); /* Found nothing?!?! Either we hang forever, or we panic. */ if (!p && !is_sysrq_oom(oc)) { dump_header(oc, NULL, NULL); panic("Out of memory and no killable processes...\n"); } if (p && p != (void *)-1UL) { oom_kill_process(oc, p, points, totalpages, NULL, "Out of memory"); /* * Give the killed process a good chance to exit before trying * to allocate memory again. */ schedule_timeout_killable(1); } return true; }

Contributors

PersonTokensPropCommitsCommitProp
david rientjesdavid rientjes15654.36%1546.88%
nick pigginnick piggin4816.72%13.12%
michal hockomichal hocko3211.15%412.50%
kosaki motohirokosaki motohiro134.53%26.25%
johannes weinerjohannes weiner113.83%26.25%
tetsuo handatetsuo handa72.44%13.12%
yaowei baiyaowei bai41.39%13.12%
rusty russellrusty russell41.39%13.12%
linus torvaldslinus torvalds31.05%13.12%
andrew mortonandrew morton31.05%13.12%
oleg nesterovoleg nesterov31.05%13.12%
balasubramani vivekanandanbalasubramani vivekanandan20.70%13.12%
andrea arcangeliandrea arcangeli10.35%13.12%
Total287100.00%32100.00%

/* * The pagefault handler calls here because it is out of memory, so kill a * memory-hogging task. If any populated zone has ZONE_OOM_LOCKED set, a * parallel oom killing is already in progress so do nothing. */
void pagefault_out_of_memory(void) { struct oom_control oc = { .zonelist = NULL, .nodemask = NULL, .gfp_mask = 0, .order = 0, }; if (mem_cgroup_oom_synchronize(true)) return; if (!mutex_trylock(&oom_lock)) return; if (!out_of_memory(&oc)) { /* * There shouldn't be any user tasks runnable while the * OOM killer is disabled, so the current task has to * be a racing OOM victim for which oom_killer_disable() * is waiting for. */ WARN_ON(test_thread_flag(TIF_MEMDIE)); } mutex_unlock(&oom_lock); }

Contributors

PersonTokensPropCommitsCommitProp
david rientjesdavid rientjes4355.84%342.86%
johannes weinerjohannes weiner1924.68%342.86%
michal hockomichal hocko1519.48%114.29%
Total77100.00%7100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
david rientjesdavid rientjes112235.65%4932.03%
michal hockomichal hocko103132.76%1912.42%
kosaki motohirokosaki motohiro1705.40%106.54%
oleg nesterovoleg nesterov1595.05%95.88%
tetsuo handatetsuo handa732.32%74.58%
pre-gitpre-git672.13%10.65%
nick pigginnick piggin621.97%31.96%
martin schwidefskymartin schwidefsky591.87%10.65%
kamezawa hiroyukikamezawa hiroyuki591.87%31.96%
johannes weinerjohannes weiner531.68%53.27%
christoph lameterchristoph lameter371.18%21.31%
yaowei baiyaowei bai321.02%10.65%
kirill a. shutemovkirill a. shutemov270.86%21.31%
andrea arcangeliandrea arcangeli230.73%10.65%
mel gormanmel gorman220.70%21.31%
vladimir davydovvladimir davydov160.51%21.31%
kurt garloffkurt garloff150.48%10.65%
jerome marchandjerome marchand130.41%10.65%
rusty russellrusty russell110.35%10.65%
andrew mortonandrew morton100.32%42.61%
linus torvaldslinus torvalds90.29%31.96%
balasubramani vivekanandanbalasubramani vivekanandan70.22%10.65%
tim schmielautim schmielau70.22%21.31%
eric w. biedermaneric w. biederman60.19%10.65%
vlastimil babkavlastimil babka60.19%10.65%
pavel emelianovpavel emelianov60.19%21.31%
daisuke nishimuradaisuke nishimura60.19%10.65%
alexey dobriyanalexey dobriyan60.19%21.31%
sha zhengjusha zhengju50.16%10.65%
david howellsdavid howells50.16%21.31%
chen jiechen jie40.13%10.65%
tejun heotejun heo30.10%10.65%
paul jacksonpaul jackson30.10%10.65%
matthias kaehlckematthias kaehlcke30.10%10.65%
ingo molnaringo molnar20.06%10.65%
andrew g. morganandrew g. morgan10.03%10.65%
frantisek hrbatafrantisek hrbata10.03%10.65%
cyrill gorcunovcyrill gorcunov10.03%10.65%
paul gortmakerpaul gortmaker10.03%10.65%
lai jiangshanlai jiangshan10.03%10.65%
eric pariseric paris10.03%10.65%
wang longwang long10.03%10.65%
joe perchesjoe perches10.03%10.65%
Total3147100.00%153100.00%
Directory: mm
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}