1
0
Fork 0

mm, oom: organize oom context into struct

There are essential elements to an oom context that are passed around to
multiple functions.

Organize these elements into a new struct, struct oom_control, that
specifies the context for an oom condition.

This patch introduces no functional change.

Signed-off-by: David Rientjes <rientjes@google.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
hifive-unleashed-5.1
David Rientjes 2015-09-08 15:00:36 -07:00 committed by Linus Torvalds
parent 2c0b80d463
commit 6e0fc46dc2
5 changed files with 98 additions and 80 deletions

View File

@ -353,9 +353,17 @@ static struct sysrq_key_op sysrq_term_op = {
static void moom_callback(struct work_struct *ignored) static void moom_callback(struct work_struct *ignored)
{ {
const gfp_t gfp_mask = GFP_KERNEL;
struct oom_control oc = {
.zonelist = node_zonelist(first_memory_node, gfp_mask),
.nodemask = NULL,
.gfp_mask = gfp_mask,
.order = 0,
.force_kill = true,
};
mutex_lock(&oom_lock); mutex_lock(&oom_lock);
if (!out_of_memory(node_zonelist(first_memory_node, GFP_KERNEL), if (!out_of_memory(&oc))
GFP_KERNEL, 0, NULL, true))
pr_info("OOM request ignored because killer is disabled\n"); pr_info("OOM request ignored because killer is disabled\n");
mutex_unlock(&oom_lock); mutex_unlock(&oom_lock);
} }

View File

@ -12,6 +12,14 @@ struct notifier_block;
struct mem_cgroup; struct mem_cgroup;
struct task_struct; struct task_struct;
struct oom_control {
struct zonelist *zonelist;
nodemask_t *nodemask;
gfp_t gfp_mask;
int order;
bool force_kill;
};
/* /*
* Types of limitations to the nodes from which allocations may occur * Types of limitations to the nodes from which allocations may occur
*/ */
@ -57,21 +65,18 @@ extern unsigned long oom_badness(struct task_struct *p,
extern int oom_kills_count(void); extern int oom_kills_count(void);
extern void note_oom_kill(void); extern void note_oom_kill(void);
extern void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, extern void oom_kill_process(struct oom_control *oc, struct task_struct *p,
unsigned int points, unsigned long totalpages, unsigned int points, unsigned long totalpages,
struct mem_cgroup *memcg, nodemask_t *nodemask, struct mem_cgroup *memcg, const char *message);
const char *message);
extern void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, extern void check_panic_on_oom(struct oom_control *oc,
int order, const nodemask_t *nodemask, enum oom_constraint constraint,
struct mem_cgroup *memcg); struct mem_cgroup *memcg);
extern enum oom_scan_t oom_scan_process_thread(struct task_struct *task, extern enum oom_scan_t oom_scan_process_thread(struct oom_control *oc,
unsigned long totalpages, const nodemask_t *nodemask, struct task_struct *task, unsigned long totalpages);
bool force_kill);
extern bool out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, extern bool out_of_memory(struct oom_control *oc);
int order, nodemask_t *mask, bool force_kill);
extern void exit_oom_victim(void); extern void exit_oom_victim(void);

View File

@ -1545,6 +1545,13 @@ static unsigned long mem_cgroup_get_limit(struct mem_cgroup *memcg)
static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
int order) int order)
{ {
struct oom_control oc = {
.zonelist = NULL,
.nodemask = NULL,
.gfp_mask = gfp_mask,
.order = order,
.force_kill = false,
};
struct mem_cgroup *iter; struct mem_cgroup *iter;
unsigned long chosen_points = 0; unsigned long chosen_points = 0;
unsigned long totalpages; unsigned long totalpages;
@ -1563,7 +1570,7 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
goto unlock; goto unlock;
} }
check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL, memcg); check_panic_on_oom(&oc, CONSTRAINT_MEMCG, memcg);
totalpages = mem_cgroup_get_limit(memcg) ? : 1; totalpages = mem_cgroup_get_limit(memcg) ? : 1;
for_each_mem_cgroup_tree(iter, memcg) { for_each_mem_cgroup_tree(iter, memcg) {
struct css_task_iter it; struct css_task_iter it;
@ -1571,8 +1578,7 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
css_task_iter_start(&iter->css, &it); css_task_iter_start(&iter->css, &it);
while ((task = css_task_iter_next(&it))) { while ((task = css_task_iter_next(&it))) {
switch (oom_scan_process_thread(task, totalpages, NULL, switch (oom_scan_process_thread(&oc, task, totalpages)) {
false)) {
case OOM_SCAN_SELECT: case OOM_SCAN_SELECT:
if (chosen) if (chosen)
put_task_struct(chosen); put_task_struct(chosen);
@ -1610,8 +1616,8 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
if (chosen) { if (chosen) {
points = chosen_points * 1000 / totalpages; points = chosen_points * 1000 / totalpages;
oom_kill_process(chosen, gfp_mask, order, points, totalpages, oom_kill_process(&oc, chosen, points, totalpages, memcg,
memcg, NULL, "Memory cgroup out of memory"); "Memory cgroup out of memory");
} }
unlock: unlock:
mutex_unlock(&oom_lock); mutex_unlock(&oom_lock);

View File

@ -196,27 +196,26 @@ unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
* Determine the type of allocation constraint. * Determine the type of allocation constraint.
*/ */
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
static enum oom_constraint constrained_alloc(struct zonelist *zonelist, static enum oom_constraint constrained_alloc(struct oom_control *oc,
gfp_t gfp_mask, nodemask_t *nodemask,
unsigned long *totalpages) unsigned long *totalpages)
{ {
struct zone *zone; struct zone *zone;
struct zoneref *z; struct zoneref *z;
enum zone_type high_zoneidx = gfp_zone(gfp_mask); enum zone_type high_zoneidx = gfp_zone(oc->gfp_mask);
bool cpuset_limited = false; bool cpuset_limited = false;
int nid; int nid;
/* Default to all available memory */ /* Default to all available memory */
*totalpages = totalram_pages + total_swap_pages; *totalpages = totalram_pages + total_swap_pages;
if (!zonelist) if (!oc->zonelist)
return CONSTRAINT_NONE; return CONSTRAINT_NONE;
/* /*
* Reach here only when __GFP_NOFAIL is used. So, we should avoid * Reach here only when __GFP_NOFAIL is used. So, we should avoid
* to kill current.We have to random task kill in this case. * to kill current.We have to random task kill in this case.
* Hopefully, CONSTRAINT_THISNODE...but no way to handle it, now. * Hopefully, CONSTRAINT_THISNODE...but no way to handle it, now.
*/ */
if (gfp_mask & __GFP_THISNODE) if (oc->gfp_mask & __GFP_THISNODE)
return CONSTRAINT_NONE; return CONSTRAINT_NONE;
/* /*
@ -224,17 +223,18 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist,
* the page allocator means a mempolicy is in effect. Cpuset policy * the page allocator means a mempolicy is in effect. Cpuset policy
* is enforced in get_page_from_freelist(). * is enforced in get_page_from_freelist().
*/ */
if (nodemask && !nodes_subset(node_states[N_MEMORY], *nodemask)) { if (oc->nodemask &&
!nodes_subset(node_states[N_MEMORY], *oc->nodemask)) {
*totalpages = total_swap_pages; *totalpages = total_swap_pages;
for_each_node_mask(nid, *nodemask) for_each_node_mask(nid, *oc->nodemask)
*totalpages += node_spanned_pages(nid); *totalpages += node_spanned_pages(nid);
return CONSTRAINT_MEMORY_POLICY; return CONSTRAINT_MEMORY_POLICY;
} }
/* Check this allocation failure is caused by cpuset's wall function */ /* Check this allocation failure is caused by cpuset's wall function */
for_each_zone_zonelist_nodemask(zone, z, zonelist, for_each_zone_zonelist_nodemask(zone, z, oc->zonelist,
high_zoneidx, nodemask) high_zoneidx, oc->nodemask)
if (!cpuset_zone_allowed(zone, gfp_mask)) if (!cpuset_zone_allowed(zone, oc->gfp_mask))
cpuset_limited = true; cpuset_limited = true;
if (cpuset_limited) { if (cpuset_limited) {
@ -246,8 +246,7 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist,
return CONSTRAINT_NONE; return CONSTRAINT_NONE;
} }
#else #else
static enum oom_constraint constrained_alloc(struct zonelist *zonelist, static enum oom_constraint constrained_alloc(struct oom_control *oc,
gfp_t gfp_mask, nodemask_t *nodemask,
unsigned long *totalpages) unsigned long *totalpages)
{ {
*totalpages = totalram_pages + total_swap_pages; *totalpages = totalram_pages + total_swap_pages;
@ -255,11 +254,10 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist,
} }
#endif #endif
enum oom_scan_t oom_scan_process_thread(struct task_struct *task, enum oom_scan_t oom_scan_process_thread(struct oom_control *oc,
unsigned long totalpages, const nodemask_t *nodemask, struct task_struct *task, unsigned long totalpages)
bool force_kill)
{ {
if (oom_unkillable_task(task, NULL, nodemask)) if (oom_unkillable_task(task, NULL, oc->nodemask))
return OOM_SCAN_CONTINUE; return OOM_SCAN_CONTINUE;
/* /*
@ -267,7 +265,7 @@ enum oom_scan_t oom_scan_process_thread(struct task_struct *task,
* Don't allow any other task to have access to the reserves. * Don't allow any other task to have access to the reserves.
*/ */
if (test_tsk_thread_flag(task, TIF_MEMDIE)) { if (test_tsk_thread_flag(task, TIF_MEMDIE)) {
if (!force_kill) if (!oc->force_kill)
return OOM_SCAN_ABORT; return OOM_SCAN_ABORT;
} }
if (!task->mm) if (!task->mm)
@ -280,7 +278,7 @@ enum oom_scan_t oom_scan_process_thread(struct task_struct *task,
if (oom_task_origin(task)) if (oom_task_origin(task))
return OOM_SCAN_SELECT; return OOM_SCAN_SELECT;
if (task_will_free_mem(task) && !force_kill) if (task_will_free_mem(task) && !oc->force_kill)
return OOM_SCAN_ABORT; return OOM_SCAN_ABORT;
return OOM_SCAN_OK; return OOM_SCAN_OK;
@ -289,12 +287,9 @@ enum oom_scan_t oom_scan_process_thread(struct task_struct *task,
/* /*
* Simple selection loop. We chose the process with the highest * Simple selection loop. We chose the process with the highest
* number of 'points'. Returns -1 on scan abort. * number of 'points'. Returns -1 on scan abort.
*
* (not docbooked, we don't want this one cluttering up the manual)
*/ */
static struct task_struct *select_bad_process(unsigned int *ppoints, static struct task_struct *select_bad_process(struct oom_control *oc,
unsigned long totalpages, const nodemask_t *nodemask, unsigned int *ppoints, unsigned long totalpages)
bool force_kill)
{ {
struct task_struct *g, *p; struct task_struct *g, *p;
struct task_struct *chosen = NULL; struct task_struct *chosen = NULL;
@ -304,8 +299,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
for_each_process_thread(g, p) { for_each_process_thread(g, p) {
unsigned int points; unsigned int points;
switch (oom_scan_process_thread(p, totalpages, nodemask, switch (oom_scan_process_thread(oc, p, totalpages)) {
force_kill)) {
case OOM_SCAN_SELECT: case OOM_SCAN_SELECT:
chosen = p; chosen = p;
chosen_points = ULONG_MAX; chosen_points = ULONG_MAX;
@ -318,7 +312,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
case OOM_SCAN_OK: case OOM_SCAN_OK:
break; break;
}; };
points = oom_badness(p, NULL, nodemask, totalpages); points = oom_badness(p, NULL, oc->nodemask, totalpages);
if (!points || points < chosen_points) if (!points || points < chosen_points)
continue; continue;
/* Prefer thread group leaders for display purposes */ /* Prefer thread group leaders for display purposes */
@ -380,13 +374,13 @@ static void dump_tasks(struct mem_cgroup *memcg, const nodemask_t *nodemask)
rcu_read_unlock(); rcu_read_unlock();
} }
static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order, static void dump_header(struct oom_control *oc, struct task_struct *p,
struct mem_cgroup *memcg, const nodemask_t *nodemask) struct mem_cgroup *memcg)
{ {
task_lock(current); task_lock(current);
pr_warning("%s invoked oom-killer: gfp_mask=0x%x, order=%d, " pr_warning("%s invoked oom-killer: gfp_mask=0x%x, order=%d, "
"oom_score_adj=%hd\n", "oom_score_adj=%hd\n",
current->comm, gfp_mask, order, current->comm, oc->gfp_mask, oc->order,
current->signal->oom_score_adj); current->signal->oom_score_adj);
cpuset_print_task_mems_allowed(current); cpuset_print_task_mems_allowed(current);
task_unlock(current); task_unlock(current);
@ -396,7 +390,7 @@ static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order,
else else
show_mem(SHOW_MEM_FILTER_NODES); show_mem(SHOW_MEM_FILTER_NODES);
if (sysctl_oom_dump_tasks) if (sysctl_oom_dump_tasks)
dump_tasks(memcg, nodemask); dump_tasks(memcg, oc->nodemask);
} }
/* /*
@ -487,10 +481,9 @@ void oom_killer_enable(void)
* Must be called while holding a reference to p, which will be released upon * Must be called while holding a reference to p, which will be released upon
* returning. * returning.
*/ */
void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, void oom_kill_process(struct oom_control *oc, struct task_struct *p,
unsigned int points, unsigned long totalpages, unsigned int points, unsigned long totalpages,
struct mem_cgroup *memcg, nodemask_t *nodemask, struct mem_cgroup *memcg, const char *message)
const char *message)
{ {
struct task_struct *victim = p; struct task_struct *victim = p;
struct task_struct *child; struct task_struct *child;
@ -514,7 +507,7 @@ void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
task_unlock(p); task_unlock(p);
if (__ratelimit(&oom_rs)) if (__ratelimit(&oom_rs))
dump_header(p, gfp_mask, order, memcg, nodemask); dump_header(oc, p, memcg);
task_lock(p); task_lock(p);
pr_err("%s: Kill process %d (%s) score %u or sacrifice child\n", pr_err("%s: Kill process %d (%s) score %u or sacrifice child\n",
@ -537,7 +530,7 @@ void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
/* /*
* oom_badness() returns 0 if the thread is unkillable * oom_badness() returns 0 if the thread is unkillable
*/ */
child_points = oom_badness(child, memcg, nodemask, child_points = oom_badness(child, memcg, oc->nodemask,
totalpages); totalpages);
if (child_points > victim_points) { if (child_points > victim_points) {
put_task_struct(victim); put_task_struct(victim);
@ -600,8 +593,7 @@ void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
/* /*
* Determines whether the kernel must panic because of the panic_on_oom sysctl. * Determines whether the kernel must panic because of the panic_on_oom sysctl.
*/ */
void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, void check_panic_on_oom(struct oom_control *oc, enum oom_constraint constraint,
int order, const nodemask_t *nodemask,
struct mem_cgroup *memcg) struct mem_cgroup *memcg)
{ {
if (likely(!sysctl_panic_on_oom)) if (likely(!sysctl_panic_on_oom))
@ -615,7 +607,7 @@ void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask,
if (constraint != CONSTRAINT_NONE) if (constraint != CONSTRAINT_NONE)
return; return;
} }
dump_header(NULL, gfp_mask, order, memcg, nodemask); dump_header(oc, NULL, memcg);
panic("Out of memory: %s panic_on_oom is enabled\n", panic("Out of memory: %s panic_on_oom is enabled\n",
sysctl_panic_on_oom == 2 ? "compulsory" : "system-wide"); sysctl_panic_on_oom == 2 ? "compulsory" : "system-wide");
} }
@ -635,22 +627,16 @@ int unregister_oom_notifier(struct notifier_block *nb)
EXPORT_SYMBOL_GPL(unregister_oom_notifier); EXPORT_SYMBOL_GPL(unregister_oom_notifier);
/** /**
* __out_of_memory - kill the "best" process when we run out of memory * out_of_memory - kill the "best" process when we run out of memory
* @zonelist: zonelist pointer * @oc: pointer to struct oom_control
* @gfp_mask: memory allocation flags
* @order: amount of memory being requested as a power of 2
* @nodemask: nodemask passed to page allocator
* @force_kill: true if a task must be killed, even if others are exiting
* *
* If we run out of memory, we have the choice between either * If we run out of memory, we have the choice between either
* killing a random task (bad), letting the system crash (worse) * killing a random task (bad), letting the system crash (worse)
* OR try to be smart about which process to kill. Note that we * 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. * don't have to be perfect here, we just have to be good.
*/ */
bool out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, bool out_of_memory(struct oom_control *oc)
int order, nodemask_t *nodemask, bool force_kill)
{ {
const nodemask_t *mpol_mask;
struct task_struct *p; struct task_struct *p;
unsigned long totalpages; unsigned long totalpages;
unsigned long freed = 0; unsigned long freed = 0;
@ -684,30 +670,29 @@ bool out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask,
* Check if there were limitations on the allocation (only relevant for * Check if there were limitations on the allocation (only relevant for
* NUMA) that may require different handling. * NUMA) that may require different handling.
*/ */
constraint = constrained_alloc(zonelist, gfp_mask, nodemask, constraint = constrained_alloc(oc, &totalpages);
&totalpages); if (constraint != CONSTRAINT_MEMORY_POLICY)
mpol_mask = (constraint == CONSTRAINT_MEMORY_POLICY) ? nodemask : NULL; oc->nodemask = NULL;
check_panic_on_oom(constraint, gfp_mask, order, mpol_mask, NULL); check_panic_on_oom(oc, constraint, NULL);
if (sysctl_oom_kill_allocating_task && current->mm && if (sysctl_oom_kill_allocating_task && current->mm &&
!oom_unkillable_task(current, NULL, nodemask) && !oom_unkillable_task(current, NULL, oc->nodemask) &&
current->signal->oom_score_adj != OOM_SCORE_ADJ_MIN) { current->signal->oom_score_adj != OOM_SCORE_ADJ_MIN) {
get_task_struct(current); get_task_struct(current);
oom_kill_process(current, gfp_mask, order, 0, totalpages, NULL, oom_kill_process(oc, current, 0, totalpages, NULL,
nodemask,
"Out of memory (oom_kill_allocating_task)"); "Out of memory (oom_kill_allocating_task)");
goto out; goto out;
} }
p = select_bad_process(&points, totalpages, mpol_mask, force_kill); p = select_bad_process(oc, &points, totalpages);
/* Found nothing?!?! Either we hang forever, or we panic. */ /* Found nothing?!?! Either we hang forever, or we panic. */
if (!p) { if (!p) {
dump_header(NULL, gfp_mask, order, NULL, mpol_mask); dump_header(oc, NULL, NULL);
panic("Out of memory and no killable processes...\n"); panic("Out of memory and no killable processes...\n");
} }
if (p != (void *)-1UL) { if (p != (void *)-1UL) {
oom_kill_process(p, gfp_mask, order, points, totalpages, NULL, oom_kill_process(oc, p, points, totalpages, NULL,
nodemask, "Out of memory"); "Out of memory");
killed = 1; killed = 1;
} }
out: out:
@ -728,13 +713,21 @@ out:
*/ */
void pagefault_out_of_memory(void) void pagefault_out_of_memory(void)
{ {
struct oom_control oc = {
.zonelist = NULL,
.nodemask = NULL,
.gfp_mask = 0,
.order = 0,
.force_kill = false,
};
if (mem_cgroup_oom_synchronize(true)) if (mem_cgroup_oom_synchronize(true))
return; return;
if (!mutex_trylock(&oom_lock)) if (!mutex_trylock(&oom_lock))
return; return;
if (!out_of_memory(NULL, 0, 0, NULL, false)) { if (!out_of_memory(&oc)) {
/* /*
* There shouldn't be any user tasks runnable while the * There shouldn't be any user tasks runnable while the
* OOM killer is disabled, so the current task has to * OOM killer is disabled, so the current task has to

View File

@ -2693,6 +2693,13 @@ static inline struct page *
__alloc_pages_may_oom(gfp_t gfp_mask, unsigned int order, __alloc_pages_may_oom(gfp_t gfp_mask, unsigned int order,
const struct alloc_context *ac, unsigned long *did_some_progress) const struct alloc_context *ac, unsigned long *did_some_progress)
{ {
struct oom_control oc = {
.zonelist = ac->zonelist,
.nodemask = ac->nodemask,
.gfp_mask = gfp_mask,
.order = order,
.force_kill = false,
};
struct page *page; struct page *page;
*did_some_progress = 0; *did_some_progress = 0;
@ -2744,8 +2751,7 @@ __alloc_pages_may_oom(gfp_t gfp_mask, unsigned int order,
goto out; goto out;
} }
/* Exhausted what can be done so it's blamo time */ /* Exhausted what can be done so it's blamo time */
if (out_of_memory(ac->zonelist, gfp_mask, order, ac->nodemask, false) if (out_of_memory(&oc) || WARN_ON_ONCE(gfp_mask & __GFP_NOFAIL))
|| WARN_ON_ONCE(gfp_mask & __GFP_NOFAIL))
*did_some_progress = 1; *did_some_progress = 1;
out: out:
mutex_unlock(&oom_lock); mutex_unlock(&oom_lock);