|
@@ -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,
|
|
|
|
- gfp_t gfp_mask, nodemask_t *nodemask,
|
|
|
|
- unsigned long *totalpages)
|
|
|
|
|
|
+static enum oom_constraint constrained_alloc(struct oom_control *oc,
|
|
|
|
+ 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,
|
|
|
|
- high_zoneidx, nodemask)
|
|
|
|
- if (!cpuset_zone_allowed(zone, gfp_mask))
|
|
|
|
|
|
+ for_each_zone_zonelist_nodemask(zone, z, oc->zonelist,
|
|
|
|
+ high_zoneidx, oc->nodemask)
|
|
|
|
+ if (!cpuset_zone_allowed(zone, oc->gfp_mask))
|
|
cpuset_limited = true;
|
|
cpuset_limited = true;
|
|
|
|
|
|
if (cpuset_limited) {
|
|
if (cpuset_limited) {
|
|
@@ -246,20 +246,18 @@ 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,
|
|
|
|
- gfp_t gfp_mask, nodemask_t *nodemask,
|
|
|
|
- unsigned long *totalpages)
|
|
|
|
|
|
+static enum oom_constraint constrained_alloc(struct oom_control *oc,
|
|
|
|
+ unsigned long *totalpages)
|
|
{
|
|
{
|
|
*totalpages = totalram_pages + total_swap_pages;
|
|
*totalpages = totalram_pages + total_swap_pages;
|
|
return CONSTRAINT_NONE;
|
|
return CONSTRAINT_NONE;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
-enum oom_scan_t oom_scan_process_thread(struct task_struct *task,
|
|
|
|
- unsigned long totalpages, const nodemask_t *nodemask,
|
|
|
|
- bool force_kill)
|
|
|
|
|
|
+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, 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,
|
|
|
|
- unsigned long totalpages, const nodemask_t *nodemask,
|
|
|
|
- bool force_kill)
|
|
|
|
|
|
+static struct task_struct *select_bad_process(struct oom_control *oc,
|
|
|
|
+ unsigned int *ppoints, unsigned long totalpages)
|
|
{
|
|
{
|
|
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,
|
|
|
|
- force_kill)) {
|
|
|
|
|
|
+ switch (oom_scan_process_thread(oc, p, totalpages)) {
|
|
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,
|
|
|
|
- struct mem_cgroup *memcg, const nodemask_t *nodemask)
|
|
|
|
|
|
+static void dump_header(struct oom_control *oc, struct task_struct *p,
|
|
|
|
+ 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,
|
|
|
|
- const char *message)
|
|
|
|
|
|
+ struct mem_cgroup *memcg, 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,
|
|
|
|
- int order, const nodemask_t *nodemask,
|
|
|
|
|
|
+void check_panic_on_oom(struct oom_control *oc, enum oom_constraint constraint,
|
|
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
|
|
|
|
- * @zonelist: zonelist pointer
|
|
|
|
- * @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
|
|
|
|
|
|
+ * 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
|
|
* 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,
|
|
|
|
- int order, nodemask_t *nodemask, bool force_kill)
|
|
|
|
|
|
+bool out_of_memory(struct oom_control *oc)
|
|
{
|
|
{
|
|
- 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,
|
|
|
|
- &totalpages);
|
|
|
|
- mpol_mask = (constraint == CONSTRAINT_MEMORY_POLICY) ? nodemask : NULL;
|
|
|
|
- check_panic_on_oom(constraint, gfp_mask, order, mpol_mask, NULL);
|
|
|
|
|
|
+ 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 &&
|
|
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,
|
|
|
|
- nodemask,
|
|
|
|
|
|
+ oom_kill_process(oc, current, 0, totalpages, NULL,
|
|
"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,
|
|
|
|
- nodemask, "Out of memory");
|
|
|
|
|
|
+ oom_kill_process(oc, p, points, totalpages, NULL,
|
|
|
|
+ "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
|