Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1417064 > unrolled thread
| Started by | Michal Hocko <mhocko@kernel.org> |
|---|---|
| First post | 2016-06-08 10:40 +0200 |
| Last post | 2016-06-08 16:50 +0200 |
| Articles | 6 — 3 participants |
Back to article view | Back to linux.kernel
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
Re: [PATCH 2/2] mm: oom: deduplicate victim selection code for memcg and global oom Michal Hocko <mhocko@kernel.org> - 2016-06-08 10:40 +0200
Re: [PATCH 2/2] mm: oom: deduplicate victim selection code for memcg and global oom Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2016-06-08 13:20 +0200
Re: [PATCH 2/2] mm: oom: deduplicate victim selection code for memcg and global oom Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2016-06-08 13:30 +0200
Re: [PATCH 2/2] mm: oom: deduplicate victim selection code for memcg and global oom Michal Hocko <mhocko@kernel.org> - 2016-06-08 16:30 +0200
Re: [PATCH 2/2] mm: oom: deduplicate victim selection code for memcg and global oom Vladimir Davydov <vdavydov@virtuozzo.com> - 2016-06-08 16:10 +0200
Re: [PATCH 2/2] mm: oom: deduplicate victim selection code for memcg and global oom Michal Hocko <mhocko@kernel.org> - 2016-06-08 16:50 +0200
| From | Michal Hocko <mhocko@kernel.org> |
|---|---|
| Date | 2016-06-08 10:40 +0200 |
| Subject | Re: [PATCH 2/2] mm: oom: deduplicate victim selection code for memcg and global oom |
| Message-ID | <rHHbR-6et-83@gated-at.bofh.it> |
On Fri 27-05-16 17:17:42, Vladimir Davydov wrote:
[...]
> @@ -970,26 +1028,25 @@ bool out_of_memory(struct oom_control *oc)
> !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,
> - "Out of memory (oom_kill_allocating_task)");
> + oom_kill_process(oc, current, 0, totalpages);
> return true;
> }
Do we really want to introduce sysctl_oom_kill_allocating_task to memcg
as well? The heuristic is quite dubious even for the global context IMHO
because it leads to a very random behavior.
> p = select_bad_process(oc, &points, totalpages);
> /* Found nothing?!?! Either we hang forever, or we panic. */
> - if (!p && !is_sysrq_oom(oc)) {
> + if (!p && !is_sysrq_oom(oc) && !oc->memcg) {
> dump_header(oc, NULL);
> panic("Out of memory and no killable processes...\n");
> }
> if (p && p != (void *)-1UL) {
> - oom_kill_process(oc, p, points, totalpages, "Out of memory");
> + oom_kill_process(oc, p, points, totalpages);
> /*
> * Give the killed process a good chance to exit before trying
> * to allocate memory again.
> */
> schedule_timeout_killable(1);
> }
> - return true;
> + return !!p;
> }
Now if you look at out_of_memory() the only shared "heuristic" with the
memcg part is the bypass for the exiting tasks. Plus both need the
oom_lock.
You have to special case oom notifiers, panic on no victim handling and
I guess the oom_kill_allocating task is not intentional either. So I
am not really sure this is an improvement. I even hate how we conflate
sysrq vs. regular global oom context together but my cleanup for that
has failed in the past.
The victim selection code can be reduced because it is basically
shared between the two, only the iterator differs. But I guess that
can be eliminated by a simple helper.
---
include/linux/oom.h | 5 +++++
mm/memcontrol.c | 47 ++++++-----------------------------------
mm/oom_kill.c | 60 ++++++++++++++++++++++++++++-------------------------
3 files changed, 43 insertions(+), 69 deletions(-)
diff --git a/include/linux/oom.h b/include/linux/oom.h
index 606137b3b778..7b3eb253ba23 100644
--- a/include/linux/oom.h
+++ b/include/linux/oom.h
@@ -34,6 +34,9 @@ struct oom_control {
* for display purposes.
*/
const int order;
+
+ struct task_struct *chosen;
+ unsigned long chosen_points;
};
/*
@@ -80,6 +83,8 @@ static inline void try_oom_reaper(struct task_struct *tsk)
}
#endif
+extern int oom_evaluate_task(struct oom_control *oc, struct task_struct *p,
+ unsigned long totalpages);
extern unsigned long oom_badness(struct task_struct *p,
struct mem_cgroup *memcg, const nodemask_t *nodemask,
unsigned long totalpages);
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 83a6a2b92301..9c51b4d11691 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1264,10 +1264,8 @@ static bool mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
.order = order,
};
struct mem_cgroup *iter;
- unsigned long chosen_points = 0;
unsigned long totalpages;
unsigned int points = 0;
- struct task_struct *chosen = NULL;
mutex_lock(&oom_lock);
@@ -1289,53 +1287,20 @@ static bool mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
struct task_struct *task;
css_task_iter_start(&iter->css, &it);
- while ((task = css_task_iter_next(&it))) {
- switch (oom_scan_process_thread(&oc, task)) {
- case OOM_SCAN_SELECT:
- if (chosen)
- put_task_struct(chosen);
- chosen = task;
- chosen_points = ULONG_MAX;
- get_task_struct(chosen);
- /* fall through */
- case OOM_SCAN_CONTINUE:
- continue;
- case OOM_SCAN_ABORT:
- css_task_iter_end(&it);
- mem_cgroup_iter_break(memcg, iter);
- if (chosen)
- put_task_struct(chosen);
- /* Set a dummy value to return "true". */
- chosen = (void *) 1;
- goto unlock;
- case OOM_SCAN_OK:
+ while ((task = css_task_iter_next(&it)))
+ if (!oom_evaluate_task(&oc, task, totalpages))
break;
- };
- points = oom_badness(task, memcg, NULL, totalpages);
- if (!points || points < chosen_points)
- continue;
- /* Prefer thread group leaders for display purposes */
- if (points == chosen_points &&
- thread_group_leader(chosen))
- continue;
-
- if (chosen)
- put_task_struct(chosen);
- chosen = task;
- chosen_points = points;
- get_task_struct(chosen);
- }
css_task_iter_end(&it);
}
- if (chosen) {
- points = chosen_points * 1000 / totalpages;
- oom_kill_process(&oc, chosen, points, totalpages,
+ if (oc.chosen) {
+ points = oc.chosen_points * 1000 / totalpages;
+ oom_kill_process(&oc, oc.chosen, points, totalpages,
"Memory cgroup out of memory");
}
unlock:
mutex_unlock(&oom_lock);
- return chosen;
+ return oc.chosen;
}
#if MAX_NUMNODES > 1
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index c11f8bdd0c12..bce3ea262110 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -296,6 +296,34 @@ enum oom_scan_t oom_scan_process_thread(struct oom_control *oc,
return OOM_SCAN_OK;
}
+int oom_evaluate_task(struct oom_control *oc, struct task_struct *p, unsigned long totalpages)
+{
+ unsigned long points;
+
+ switch (oom_scan_process_thread(oc, p)) {
+ case OOM_SCAN_SELECT:
+ points = ULONG_MAX;
+ goto select_task;
+ case OOM_SCAN_CONTINUE:
+ return 1;
+ case OOM_SCAN_ABORT:
+ return 0;
+ case OOM_SCAN_OK:
+ break;
+ };
+ points = oom_badness(p, oc->memcg, oc->nodemask, totalpages);
+ if (points || points < oc->chosen_points)
+ return 1;
+
+select_task:
+ if (oc->chosen)
+ put_task_struct(oc->chosen);
+ get_task_struct(p);
+ oc->chosen = p;
+ oc->chosen_points = points;
+ return 1;
+}
+
/*
* Simple selection loop. We chose the process with the highest
* number of 'points'. Returns -1 on scan abort.
@@ -304,39 +332,15 @@ 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)) {
- 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:
+ for_each_process(p)
+ if (!oom_evaluate_task(oc, p, totalpages))
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;
+ *ppoints = oc->chosen_points * 1000 / totalpages;
+ return oc->chosen;
}
/**
--
Michal Hocko
SUSE Labs
[toc] | [next] | [standalone]
| From | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
|---|---|
| Date | 2016-06-08 13:20 +0200 |
| Subject | Re: [PATCH 2/2] mm: oom: deduplicate victim selection code for memcg and global oom |
| Message-ID | <rHJGG-7U2-7@gated-at.bofh.it> |
| In reply to | #1417064 |
Michal Hocko wrote:
> The victim selection code can be reduced because it is basically
> shared between the two, only the iterator differs. But I guess that
> can be eliminated by a simple helper.
Thank you for CC: me. I like this clean up.
> ---
> include/linux/oom.h | 5 +++++
> mm/memcontrol.c | 47 ++++++-----------------------------------
> mm/oom_kill.c | 60 ++++++++++++++++++++++++++++-------------------------
> 3 files changed, 43 insertions(+), 69 deletions(-)
I think we can apply your version with below changes folded into your version.
(I think totalpages argument can be passed via oom_control as well. Also, according to
http://lkml.kernel.org/r/201602192336.EJF90671.HMFLFSVOFJOtOQ@I-love.SAKURA.ne.jp ,
we can safely replace oc->memcg in oom_badness() in oom_evaluate_task() with NULL. )
include/linux/oom.h | 10 ----------
mm/memcontrol.c | 7 +++++--
mm/oom_kill.c | 14 ++++++++++++--
3 files changed, 17 insertions(+), 14 deletions(-)
diff --git a/include/linux/oom.h b/include/linux/oom.h
index 7b3eb25..77e98a0 100644
--- a/include/linux/oom.h
+++ b/include/linux/oom.h
@@ -49,13 +49,6 @@ enum oom_constraint {
CONSTRAINT_MEMCG,
};
-enum oom_scan_t {
- OOM_SCAN_OK, /* scan thread and find its badness */
- OOM_SCAN_CONTINUE, /* do not consider thread for oom kill */
- OOM_SCAN_ABORT, /* abort the iteration and return */
- OOM_SCAN_SELECT, /* always select this thread first */
-};
-
extern struct mutex oom_lock;
static inline void set_current_oom_origin(void)
@@ -96,9 +89,6 @@ extern void oom_kill_process(struct oom_control *oc, struct task_struct *p,
extern void check_panic_on_oom(struct oom_control *oc,
enum oom_constraint constraint);
-extern enum oom_scan_t oom_scan_process_thread(struct oom_control *oc,
- struct task_struct *task);
-
extern bool out_of_memory(struct oom_control *oc);
extern void exit_oom_victim(struct task_struct *tsk);
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 9c51b4d..f3482a2 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1288,12 +1288,15 @@ static bool mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
css_task_iter_start(&iter->css, &it);
while ((task = css_task_iter_next(&it)))
- if (!oom_evaluate_task(&oc, task, totalpages))
+ if (!oom_evaluate_task(&oc, task, totalpages)) {
+ css_task_iter_end(&it);
+ mem_cgroup_iter_break(memcg, iter);
break;
+ }
css_task_iter_end(&it);
}
- if (oc.chosen) {
+ if (oc.chosen && oc.chosen != (void *) -1UL) {
points = oc.chosen_points * 1000 / totalpages;
oom_kill_process(&oc, oc.chosen, points, totalpages,
"Memory cgroup out of memory");
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index bce3ea2..f634bca 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -273,8 +273,15 @@ static enum oom_constraint constrained_alloc(struct oom_control *oc,
}
#endif
-enum oom_scan_t oom_scan_process_thread(struct oom_control *oc,
- struct task_struct *task)
+enum oom_scan_t {
+ OOM_SCAN_OK, /* scan thread and find its badness */
+ OOM_SCAN_CONTINUE, /* do not consider thread for oom kill */
+ OOM_SCAN_ABORT, /* abort the iteration and return */
+ OOM_SCAN_SELECT, /* always select this thread first */
+};
+
+static enum oom_scan_t oom_scan_process_thread(struct oom_control *oc,
+ struct task_struct *task)
{
if (oom_unkillable_task(task, NULL, oc->nodemask))
return OOM_SCAN_CONTINUE;
@@ -307,6 +314,9 @@ int oom_evaluate_task(struct oom_control *oc, struct task_struct *p, unsigned lo
case OOM_SCAN_CONTINUE:
return 1;
case OOM_SCAN_ABORT:
+ if (oc->chosen)
+ put_task_struct(oc->chosen);
+ oc->chosen = (void *) -1UL;
return 0;
case OOM_SCAN_OK:
break;
[toc] | [prev] | [next] | [standalone]
| From | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
|---|---|
| Date | 2016-06-08 13:30 +0200 |
| Subject | Re: [PATCH 2/2] mm: oom: deduplicate victim selection code for memcg and global oom |
| Message-ID | <rHJQm-7Xe-37@gated-at.bofh.it> |
| In reply to | #1417299 |
Tetsuo Handa wrote:
> Michal Hocko wrote:
> > The victim selection code can be reduced because it is basically
> > shared between the two, only the iterator differs. But I guess that
> > can be eliminated by a simple helper.
>
> Thank you for CC: me. I like this clean up.
>
> > ---
> > include/linux/oom.h | 5 +++++
> > mm/memcontrol.c | 47 ++++++-----------------------------------
> > mm/oom_kill.c | 60 ++++++++++++++++++++++++++++-------------------------
> > 3 files changed, 43 insertions(+), 69 deletions(-)
>
> I think we can apply your version with below changes folded into your version.
> (I think totalpages argument can be passed via oom_control as well. Also, according to
> http://lkml.kernel.org/r/201602192336.EJF90671.HMFLFSVOFJOtOQ@I-love.SAKURA.ne.jp ,
> we can safely replace oc->memcg in oom_badness() in oom_evaluate_task() with NULL. )
>
> include/linux/oom.h | 10 ----------
> mm/memcontrol.c | 7 +++++--
> mm/oom_kill.c | 14 ++++++++++++--
> 3 files changed, 17 insertions(+), 14 deletions(-)
>
> diff --git a/include/linux/oom.h b/include/linux/oom.h
> index 7b3eb25..77e98a0 100644
> --- a/include/linux/oom.h
> +++ b/include/linux/oom.h
> @@ -49,13 +49,6 @@ enum oom_constraint {
> CONSTRAINT_MEMCG,
> };
>
> -enum oom_scan_t {
> - OOM_SCAN_OK, /* scan thread and find its badness */
> - OOM_SCAN_CONTINUE, /* do not consider thread for oom kill */
> - OOM_SCAN_ABORT, /* abort the iteration and return */
> - OOM_SCAN_SELECT, /* always select this thread first */
> -};
> -
> extern struct mutex oom_lock;
>
> static inline void set_current_oom_origin(void)
> @@ -96,9 +89,6 @@ extern void oom_kill_process(struct oom_control *oc, struct task_struct *p,
> extern void check_panic_on_oom(struct oom_control *oc,
> enum oom_constraint constraint);
>
> -extern enum oom_scan_t oom_scan_process_thread(struct oom_control *oc,
> - struct task_struct *task);
> -
> extern bool out_of_memory(struct oom_control *oc);
>
> extern void exit_oom_victim(struct task_struct *tsk);
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 9c51b4d..f3482a2 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -1288,12 +1288,15 @@ static bool mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
>
> css_task_iter_start(&iter->css, &it);
> while ((task = css_task_iter_next(&it)))
> - if (!oom_evaluate_task(&oc, task, totalpages))
> + if (!oom_evaluate_task(&oc, task, totalpages)) {
> + css_task_iter_end(&it);
Oops. Duplicated css_task_iter_end() calls. If it is safe to reverse ordering of
css_task_iter_end(&it) and mem_cgroup_iter_break(memcg, iter), removing this
css_task_iter_end(&it) line is the simplest fix.
> + mem_cgroup_iter_break(memcg, iter);
> break;
> + }
> css_task_iter_end(&it);
> }
>
> - if (oc.chosen) {
> + if (oc.chosen && oc.chosen != (void *) -1UL) {
> points = oc.chosen_points * 1000 / totalpages;
> oom_kill_process(&oc, oc.chosen, points, totalpages,
> "Memory cgroup out of memory");
> diff --git a/mm/oom_kill.c b/mm/oom_kill.c
> index bce3ea2..f634bca 100644
> --- a/mm/oom_kill.c
> +++ b/mm/oom_kill.c
> @@ -273,8 +273,15 @@ static enum oom_constraint constrained_alloc(struct oom_control *oc,
> }
> #endif
>
> -enum oom_scan_t oom_scan_process_thread(struct oom_control *oc,
> - struct task_struct *task)
> +enum oom_scan_t {
> + OOM_SCAN_OK, /* scan thread and find its badness */
> + OOM_SCAN_CONTINUE, /* do not consider thread for oom kill */
> + OOM_SCAN_ABORT, /* abort the iteration and return */
> + OOM_SCAN_SELECT, /* always select this thread first */
> +};
> +
> +static enum oom_scan_t oom_scan_process_thread(struct oom_control *oc,
> + struct task_struct *task)
> {
> if (oom_unkillable_task(task, NULL, oc->nodemask))
> return OOM_SCAN_CONTINUE;
> @@ -307,6 +314,9 @@ int oom_evaluate_task(struct oom_control *oc, struct task_struct *p, unsigned lo
> case OOM_SCAN_CONTINUE:
> return 1;
> case OOM_SCAN_ABORT:
> + if (oc->chosen)
> + put_task_struct(oc->chosen);
> + oc->chosen = (void *) -1UL;
> return 0;
> case OOM_SCAN_OK:
> break;
>
[toc] | [prev] | [next] | [standalone]
| From | Michal Hocko <mhocko@kernel.org> |
|---|---|
| Date | 2016-06-08 16:30 +0200 |
| Message-ID | <rHMEy-1il-17@gated-at.bofh.it> |
| In reply to | #1417299 |
On Wed 08-06-16 20:18:24, Tetsuo Handa wrote:
> Michal Hocko wrote:
> > The victim selection code can be reduced because it is basically
> > shared between the two, only the iterator differs. But I guess that
> > can be eliminated by a simple helper.
>
> Thank you for CC: me. I like this clean up.
>
> > ---
> > include/linux/oom.h | 5 +++++
> > mm/memcontrol.c | 47 ++++++-----------------------------------
> > mm/oom_kill.c | 60 ++++++++++++++++++++++++++++-------------------------
> > 3 files changed, 43 insertions(+), 69 deletions(-)
>
> I think we can apply your version with below changes folded into your version.
> (I think totalpages argument can be passed via oom_control as well. Also, according to
> http://lkml.kernel.org/r/201602192336.EJF90671.HMFLFSVOFJOtOQ@I-love.SAKURA.ne.jp ,
> we can safely replace oc->memcg in oom_badness() in oom_evaluate_task() with NULL. )
yes oom_badness can never see a task from outside of the memcg
hierarchy.
[...]
> +static enum oom_scan_t oom_scan_process_thread(struct oom_control *oc,
> + struct task_struct *task)
> {
> if (oom_unkillable_task(task, NULL, oc->nodemask))
> return OOM_SCAN_CONTINUE;
> @@ -307,6 +314,9 @@ int oom_evaluate_task(struct oom_control *oc, struct task_struct *p, unsigned lo
> case OOM_SCAN_CONTINUE:
> return 1;
> case OOM_SCAN_ABORT:
> + if (oc->chosen)
> + put_task_struct(oc->chosen);
> + oc->chosen = (void *) -1UL;
true including the memcg fixup.
> return 0;
> case OOM_SCAN_OK:
> break;
Thanks! I've updated the patch locally but I will wait for Vladimir what
he thinks about this wrt. the original approach.
--
Michal Hocko
SUSE Labs
[toc] | [prev] | [next] | [standalone]
| From | Vladimir Davydov <vdavydov@virtuozzo.com> |
|---|---|
| Date | 2016-06-08 16:10 +0200 |
| Message-ID | <rHMlc-1bu-37@gated-at.bofh.it> |
| In reply to | #1417064 |
On Wed, Jun 08, 2016 at 10:33:34AM +0200, Michal Hocko wrote:
> On Fri 27-05-16 17:17:42, Vladimir Davydov wrote:
> [...]
> > @@ -970,26 +1028,25 @@ bool out_of_memory(struct oom_control *oc)
> > !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,
> > - "Out of memory (oom_kill_allocating_task)");
> > + oom_kill_process(oc, current, 0, totalpages);
> > return true;
> > }
>
> Do we really want to introduce sysctl_oom_kill_allocating_task to memcg
> as well?
Not sure, but why not? We take into account dump_tasks and panic_on_oom
on memcg oom so why should we treat this sysctl differently?
> The heuristic is quite dubious even for the global context IMHO
> because it leads to a very random behavior.
>
> > p = select_bad_process(oc, &points, totalpages);
> > /* Found nothing?!?! Either we hang forever, or we panic. */
> > - if (!p && !is_sysrq_oom(oc)) {
> > + if (!p && !is_sysrq_oom(oc) && !oc->memcg) {
> > dump_header(oc, NULL);
> > panic("Out of memory and no killable processes...\n");
> > }
> > if (p && p != (void *)-1UL) {
> > - oom_kill_process(oc, p, points, totalpages, "Out of memory");
> > + oom_kill_process(oc, p, points, totalpages);
> > /*
> > * Give the killed process a good chance to exit before trying
> > * to allocate memory again.
> > */
> > schedule_timeout_killable(1);
> > }
> > - return true;
> > + return !!p;
> > }
>
> Now if you look at out_of_memory() the only shared "heuristic" with the
> memcg part is the bypass for the exiting tasks.
bypass exiting task (task_will_free_mem)
check for panic (check_panic_on_oom)
oom badness evaluation (oom_scan_process_thread or oom_evaluate_task
after your patch)
points calculation + kill (oom_kill_process)
And if you need to modify any of these function calls or add yet another
check, you have to do it twice. Ugly.
> Plus both need the oom_lock.
I believe locking could be unified for global/memcg oom cases too.
> You have to special case oom notifiers, panic on no victim handling and
> I guess the oom_kill_allocating task is not intentional either. So I
> am not really sure this is an improvement. I even hate how we conflate
> sysrq vs. regular global oom context together but my cleanup for that
> has failed in the past.
>
> The victim selection code can be reduced because it is basically
> shared between the two, only the iterator differs. But I guess that
> can be eliminated by a simple helper.
IMHO exporting a bunch of very oom-specific helpers (like those I
enumerated above), partially revealing oom implementation, instead of
well defined memcg helpers that could be reused anywhere else looks
ugly. It's like having shrink_zone implementation both in vmscan.c and
memcontrol.c with shrink_slab, shrink_lruvec, etc. exported, because we
need to iterate over cgroups there.
[toc] | [prev] | [next] | [standalone]
| From | Michal Hocko <mhocko@kernel.org> |
|---|---|
| Date | 2016-06-08 16:50 +0200 |
| Message-ID | <rHMXU-1pV-29@gated-at.bofh.it> |
| In reply to | #1417476 |
On Wed 08-06-16 16:52:04, Vladimir Davydov wrote:
> On Wed, Jun 08, 2016 at 10:33:34AM +0200, Michal Hocko wrote:
> > On Fri 27-05-16 17:17:42, Vladimir Davydov wrote:
> > [...]
> > > @@ -970,26 +1028,25 @@ bool out_of_memory(struct oom_control *oc)
> > > !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,
> > > - "Out of memory (oom_kill_allocating_task)");
> > > + oom_kill_process(oc, current, 0, totalpages);
> > > return true;
> > > }
> >
> > Do we really want to introduce sysctl_oom_kill_allocating_task to memcg
> > as well?
>
> Not sure, but why not? We take into account dump_tasks and panic_on_oom
> on memcg oom so why should we treat this sysctl differently?
Well, for one thing nobody has requested that and it would be a user
visible change which might be unexpected. And as already said I think it
was a mistake to introduce this sysctl in the first place. The behavior
is so random that I am even not sure it is usable in the real life.
Spreading it more doesn't sound like a good idea to me.
[...]
> > Now if you look at out_of_memory() the only shared "heuristic" with the
> > memcg part is the bypass for the exiting tasks.
>
> bypass exiting task (task_will_free_mem)
> check for panic (check_panic_on_oom)
> oom badness evaluation (oom_scan_process_thread or oom_evaluate_task
> after your patch)
> points calculation + kill (oom_kill_process)
>
> And if you need to modify any of these function calls or add yet another
> check, you have to do it twice. Ugly.
Ideally all those changes would happen inside those helpers. Also if you
look at out_of_memory and mem_cgroup_out_of_memory it is much easier to
follow the later one because it doesn't have that different combinations
of heuristic which only make sense for sysrq or global oom.
> > Plus both need the oom_lock.
>
> I believe locking could be unified for global/memcg oom cases too.
>
> > You have to special case oom notifiers, panic on no victim handling and
> > I guess the oom_kill_allocating task is not intentional either. So I
> > am not really sure this is an improvement. I even hate how we conflate
> > sysrq vs. regular global oom context together but my cleanup for that
> > has failed in the past.
> >
> > The victim selection code can be reduced because it is basically
> > shared between the two, only the iterator differs. But I guess that
> > can be eliminated by a simple helper.
>
> IMHO exporting a bunch of very oom-specific helpers (like those I
> enumerated above), partially revealing oom implementation, instead of
> well defined memcg helpers that could be reused anywhere else looks
> ugly. It's like having shrink_zone implementation both in vmscan.c and
> memcontrol.c with shrink_slab, shrink_lruvec, etc. exported, because we
> need to iterate over cgroups there.
I agree that the API for OOM killer parts is not really great. I am just
little bit afraid that iterators are just over engineered. I am even not
sure whethers those have any other potential users. The diffstat of the
cleanup I have here right now sounds really encouranging.
---
include/linux/oom.h | 17 ++++-------
mm/memcontrol.c | 48 +++--------------------------
mm/oom_kill.c | 87 ++++++++++++++++++++++++++++++-----------------------
3 files changed, 60 insertions(+), 92 deletions(-)
compared to yours
include/linux/memcontrol.h | 15 ++++
include/linux/oom.h | 51 -------------
mm/memcontrol.c | 112 ++++++++++-----------------
mm/oom_kill.c | 183 +++++++++++++++++++++++++++++----------------
4 files changed, 176 insertions(+), 185 deletions(-)
we save more LOC with a smaller patch. I know this is not an absolute
metric but I would rather go with simplicity than an elaborate
APIs. This is all pretty much mm/memcg internal.
Anyway I do not have strong opinion and will not insist. I can post
the full cleanup with suggestions from Tetsuo integrated if you are
interested.
--
Michal Hocko
SUSE Labs
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web