* [RFC PATCH 1/8] memcg: Enable fine-grained control of over memory.high action
2020-08-17 14:08 [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control Waiman Long
@ 2020-08-17 14:08 ` Waiman Long
2020-08-17 14:30 ` Chris Down
2020-08-17 16:44 ` Shakeel Butt
2020-08-17 14:08 ` [RFC PATCH 2/8] memcg, mm: Return ENOMEM or delay if memcg_over_limit Waiman Long
` (6 subsequent siblings)
7 siblings, 2 replies; 42+ messages in thread
From: Waiman Long @ 2020-08-17 14:08 UTC (permalink / raw)
To: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot
Cc: linux-kernel, linux-doc, linux-fsdevel, cgroups, linux-mm,
Waiman Long
Memory controller can be used to control and limit the amount of
physical memory used by a task. When a limit is set in "memory.high"
in a non-root memory cgroup, the memory controller will try to reclaim
memory if the limit has been exceeded. Normally, that will be enough
to keep the physical memory consumption of tasks in the memory cgroup
to be around or below the "memory.high" limit.
Sometimes, memory reclaim may not be able to recover memory in a rate
that can catch up to the physical memory allocation rate especially
when rotating disks are used for swapping or writing dirty pages. In
this case, the physical memory consumption will keep on increasing.
When it reaches "memory.max" or the system is really running out of
memory, the OOM killer will be invoked to kill some tasks to free up
additional memory. However, one has little control of which tasks are
going to be killed by an OOM killer.
Users who do not want the OOM killer to be invoked to kill random tasks
in an out-of-memory situation will require a better way to manage memory
and deal with applications that are out of control in term of physical
memory consumption rate.
A new set of prctl(2) commands are added to provide a facility to
allow users to manage the physical memory consumption of each of their
applications and control the mitigation actions that should be taken
when those applications consume more physical memory than what they
are supposed to use.
The new prctl(2) commands are PR_SET_MEMCONTROL and PR_GET_MEMCONTROL
to set the memory control parameters and retrieve those parameters
respectively. The four possible mitigation actions for a task that
exceeds their designated memory limit are:
1) Return ENOMEM for some syscalls that allocate or handle memory
2) Slow down the process for memory reclaim to catch up
3) Send a specific signal to the task
4) Kill the task
The parameters that can be specified in the new PR_SET_MEMCONTROL
commands are:
arg2 - the mitigation action (bits 0-7), signal number (bits 8-15)
and flags (bits 16-31).
arg3 - the additional memory limit (in bytes) that will be added to
memory.high as the real limit that will trigger the mitigation
action.
The PR_MEMFLAG_SIGCONT flag is used to specify continuous signal delivery
instead of a one-shot signal.
Signed-off-by: Waiman Long <longman@redhat.com>
---
include/linux/memcontrol.h | 4 ++
include/linux/sched.h | 7 +++
include/uapi/linux/prctl.h | 37 ++++++++++++
kernel/sys.c | 16 ++++++
mm/memcontrol.c | 114 +++++++++++++++++++++++++++++++++++++
5 files changed, 178 insertions(+)
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index d0b036123c6a..40e6ceb8209b 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -445,6 +445,10 @@ void mem_cgroup_uncharge_list(struct list_head *page_list);
void mem_cgroup_migrate(struct page *oldpage, struct page *newpage);
+long mem_cgroup_over_high_get(struct task_struct *task, unsigned long item);
+long mem_cgroup_over_high_set(struct task_struct *task, unsigned long action,
+ unsigned long limit);
+
static struct mem_cgroup_per_node *
mem_cgroup_nodeinfo(struct mem_cgroup *memcg, int nid)
{
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 93ecd930efd3..c79d606d27ab 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1265,6 +1265,13 @@ struct task_struct {
/* Number of pages to reclaim on returning to userland: */
unsigned int memcg_nr_pages_over_high;
+ /* Memory over-high action, flags, signal and limit */
+ unsigned char memcg_over_high_action;
+ unsigned char memcg_over_high_signal;
+ unsigned short memcg_over_high_flags;
+ unsigned int memcg_over_high_climit;
+ unsigned int memcg_over_limit;
+
/* Used by memcontrol for targeted memcg charge: */
struct mem_cgroup *active_memcg;
#endif
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index 07b4f8131e36..87970ae7b32c 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -238,4 +238,41 @@ struct prctl_mm_map {
#define PR_SET_IO_FLUSHER 57
#define PR_GET_IO_FLUSHER 58
+/* Per task fine-grained memory cgroup control */
+#define PR_GET_MEMCONTROL 59
+#define PR_SET_MEMCONTROL 60
+
+/*
+ * PR_SET_MEMCONTROL:
+ * 2 parameters are passed:
+ * - Action word
+ * - Memory cgroup additional memory limit
+ *
+ * The action word consists of 3 bit fields:
+ * - Bits 0-7 : over-memory-limit action code
+ * - Bits 8-15: signal number
+ * - Bits 16-32: action flags
+ */
+
+/* Control values for PR_SET_MEMCONTROL over limit action */
+# define PR_MEMACT_NONE 0
+# define PR_MEMACT_ENOMEM 1 /* Deny memory request */
+# define PR_MEMACT_SLOWDOWN 2 /* Slow down the process */
+# define PR_MEMACT_SIGNAL 3 /* Send signal */
+# define PR_MEMACT_KILL 4 /* Kill the process */
+# define PR_MEMACT_MAX PR_MEMACT_KILL
+
+/* Flags for PR_SET_MEMCONTROL */
+# define PR_MEMFLAG_SIGCONT (1UL << 0) /* Continuous signal delivery */
+# define PR_MEMFLAG_MASK PR_MEMFLAG_SIGCONT
+
+/* Action word masks */
+# define PR_MEMACT_MASK 0xff
+# define PR_MEMACT_SIG_SHIFT 8
+# define PR_MEMACT_FLG_SHIFT 16
+
+/* Return specified value for PR_GET_MEMCONTROL */
+# define PR_MEMGET_ACTION 0
+# define PR_MEMGET_CLIMIT 1
+
#endif /* _LINUX_PRCTL_H */
diff --git a/kernel/sys.c b/kernel/sys.c
index ca11af9d815d..644b86235d7f 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -64,6 +64,10 @@
#include <linux/nospec.h>
+#ifdef CONFIG_MEMCG
+#include <linux/memcontrol.h>
+#endif
+
#include <linux/kmsg_dump.h>
/* Move somewhere else to avoid recompiling? */
#include <generated/utsrelease.h>
@@ -2530,6 +2534,18 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
error = (current->flags & PR_IO_FLUSHER) == PR_IO_FLUSHER;
break;
+#ifdef CONFIG_MEMCG
+ case PR_GET_MEMCONTROL:
+ if (arg3 || arg4 || arg5)
+ return -EINVAL;
+ error = mem_cgroup_over_high_get(me, arg2);
+ break;
+ case PR_SET_MEMCONTROL:
+ if (arg4 || arg5)
+ return -EINVAL;
+ error = mem_cgroup_over_high_set(me, arg2, arg3);
+ break;
+#endif
default:
error = -EINVAL;
break;
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index b807952b4d43..1106dac024ac 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -59,6 +59,7 @@
#include <linux/tracehook.h>
#include <linux/psi.h>
#include <linux/seq_buf.h>
+#include <linux/prctl.h>
#include "internal.h"
#include <net/sock.h>
#include <net/ip.h>
@@ -2628,6 +2629,71 @@ void mem_cgroup_handle_over_high(void)
css_put(&memcg->css);
}
+/*
+ * Task specific action when over the high limit.
+ * Return true if an action has been taken or further check is not needed,
+ * false otherwise.
+ */
+static bool __mem_cgroup_over_high_action(struct mem_cgroup *memcg, u8 action)
+{
+ unsigned long mem;
+ bool ret = false;
+ struct mm_struct *mm = get_task_mm(current);
+ u8 signal = READ_ONCE(current->memcg_over_high_signal);
+ u16 flags = READ_ONCE(current->memcg_over_high_flags);
+ u32 limit = READ_ONCE(current->memcg_over_high_climit);
+
+ if (!mm)
+ return true; /* No more check is needed */
+
+ current->memcg_over_limit = false;
+ if ((action == PR_MEMACT_SIGNAL) && !signal)
+ goto out;
+
+ mem = page_counter_read(&memcg->memory);
+ if (mem <= memcg->memory.high + limit)
+ goto out;
+
+ ret = true;
+ switch (action) {
+ case PR_MEMACT_ENOMEM:
+ WRITE_ONCE(current->memcg_over_limit, true);
+ break;
+ case PR_MEMACT_SLOWDOWN:
+ /* Slow down by yielding the cpu */
+ set_tsk_need_resched(current);
+ set_preempt_need_resched();
+ break;
+ case PR_MEMACT_KILL:
+ signal = SIGKILL;
+ fallthrough;
+ case PR_MEMACT_SIGNAL:
+ force_sig(signal);
+
+ /* Deliver signal only once if !PR_MEMFLAG_SIGCONT */
+ if (!(flags & PR_MEMFLAG_SIGCONT))
+ WRITE_ONCE(current->memcg_over_high_signal, 0);
+ break;
+ }
+
+out:
+ mmput(mm);
+ return ret;
+}
+
+/*
+ * Return true if an action has been taken or further check is not needed,
+ * false otherwise.
+ */
+static inline bool mem_cgroup_over_high_action(struct mem_cgroup *memcg)
+{
+ u8 action = READ_ONCE(current->memcg_over_high_action);
+
+ if (!action)
+ return true; /* No more check is needed */
+ return __mem_cgroup_over_high_action(memcg, action);
+}
+
static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask,
unsigned int nr_pages)
{
@@ -2639,6 +2705,7 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask,
unsigned long nr_reclaimed;
bool may_swap = true;
bool drained = false;
+ bool taken = false;
unsigned long pflags;
if (mem_cgroup_is_root(memcg))
@@ -2797,6 +2864,9 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask,
swap_high = page_counter_read(&memcg->swap) >
READ_ONCE(memcg->swap.high);
+ if (mem_high && !taken)
+ taken = mem_cgroup_over_high_action(memcg);
+
/* Don't bother a random interrupted task */
if (in_interrupt()) {
if (mem_high) {
@@ -6959,6 +7029,50 @@ void mem_cgroup_sk_free(struct sock *sk)
css_put(&sk->sk_memcg->css);
}
+/*
+ * Get and set cgroup memory-over-high attributes.
+ */
+long mem_cgroup_over_high_get(struct task_struct *task, unsigned long item)
+{
+ switch (item) {
+ case PR_MEMGET_ACTION:
+ return task->memcg_over_high_action |
+ (task->memcg_over_high_signal << PR_MEMACT_SIG_SHIFT) |
+ (task->memcg_over_high_flags << PR_MEMACT_FLG_SHIFT);
+
+ case PR_MEMGET_CLIMIT:
+ return (long)task->memcg_over_high_climit * PAGE_SIZE;
+ }
+ return -EINVAL;
+}
+
+long mem_cgroup_over_high_set(struct task_struct *task, unsigned long action,
+ unsigned long limit)
+{
+ unsigned char cmd = action & PR_MEMACT_MASK;
+ unsigned char sig = (action >> PR_MEMACT_SIG_SHIFT) & PR_MEMACT_MASK;
+ unsigned short flags = action >> PR_MEMACT_FLG_SHIFT;
+
+ if ((cmd > PR_MEMACT_MAX) || (flags & ~PR_MEMFLAG_MASK) ||
+ (sig >= _NSIG))
+ return -EINVAL;
+
+ WRITE_ONCE(task->memcg_over_high_action, cmd);
+ WRITE_ONCE(task->memcg_over_high_signal, sig);
+ WRITE_ONCE(task->memcg_over_high_flags, flags);
+
+ if (cmd == PR_MEMACT_NONE) {
+ WRITE_ONCE(task->memcg_over_high_climit, 0);
+ } else {
+ /*
+ * Convert limits to # of pages
+ */
+ limit = DIV_ROUND_UP(limit, PAGE_SIZE);
+ WRITE_ONCE(task->memcg_over_high_climit, limit);
+ }
+ return 0;
+}
+
/**
* mem_cgroup_charge_skmem - charge socket memory
* @memcg: memcg to charge
--
2.18.1
^ permalink raw reply related [flat|nested] 42+ messages in thread* Re: [RFC PATCH 1/8] memcg: Enable fine-grained control of over memory.high action
2020-08-17 14:08 ` [RFC PATCH 1/8] memcg: Enable fine-grained control of over memory.high action Waiman Long
@ 2020-08-17 14:30 ` Chris Down
2020-08-17 15:38 ` Waiman Long
2020-08-17 16:44 ` Shakeel Butt
1 sibling, 1 reply; 42+ messages in thread
From: Chris Down @ 2020-08-17 14:30 UTC (permalink / raw)
To: Waiman Long
Cc: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot, linux-kernel, linux-doc,
linux-fsdevel, cgroups, linux-mm
Astractly, I think this really overcomplicates the API a lot. If these are
truly generally useful (and I think that remains to be demonstrated), they
should be additions to the existing API, rather than a sidestep with prctl.
I also worry about some other more concrete things:
1. Doesn't this allow unprivileged applications to potentially bypass
memory.high constraints set by a system administrator?
2. What's the purpose of PR_MEMACT_KILL, compared to memory.max?
3. Why add this entirely separate signal delivery path when we already have
eventfd/poll/inotify support, which makes a lot more sense for modern
applications?
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 1/8] memcg: Enable fine-grained control of over memory.high action
2020-08-17 14:30 ` Chris Down
@ 2020-08-17 15:38 ` Waiman Long
2020-08-17 16:11 ` Chris Down
0 siblings, 1 reply; 42+ messages in thread
From: Waiman Long @ 2020-08-17 15:38 UTC (permalink / raw)
To: Chris Down
Cc: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot, linux-kernel, linux-doc,
linux-fsdevel, cgroups, linux-mm
On 8/17/20 10:30 AM, Chris Down wrote:
> Astractly, I think this really overcomplicates the API a lot. If these
> are truly generally useful (and I think that remains to be
> demonstrated), they should be additions to the existing API, rather
> than a sidestep with prctl.
This patchset is derived from customer requests. With existing API, I
suppose you mean the memory cgroup API. Right? The reason to use prctl()
is that there are users out there who want some kind of per-process
control instead of for a whole group of processes unless the users try
to create one cgroup per process which is not very efficient.
>
> I also worry about some other more concrete things:
>
> 1. Doesn't this allow unprivileged applications to potentially bypass
> memory.high constraints set by a system administrator?
The memory.high constraint is for triggering memory reclaim. The new
mitigation actions introduced by this patchset will only be applied if
memory reclaim alone fails to limit the physical memory consumption. The
current memory cgroup memory reclaim code will not be affected by this
patchset.
> 2. What's the purpose of PR_MEMACT_KILL, compared to memory.max?
A user can use this to specify which processes are less important and
can be sacrificed first instead of the other more important ones in case
they are really in a OOM situation. IOW, users can specify the order
where OOM kills can happen.
> 3. Why add this entirely separate signal delivery path when we already
> have eventfd/poll/inotify support, which makes a lot more sense for
> modern applications?
Good question, I will look further into this to see if it can be
applicable in this case.
Cheers,
Longman
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [RFC PATCH 1/8] memcg: Enable fine-grained control of over memory.high action
2020-08-17 15:38 ` Waiman Long
@ 2020-08-17 16:11 ` Chris Down
0 siblings, 0 replies; 42+ messages in thread
From: Chris Down @ 2020-08-17 16:11 UTC (permalink / raw)
To: Waiman Long
Cc: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot, linux-kernel, linux-doc,
linux-fsdevel, cgroups, linux-mm
Waiman Long writes:
>On 8/17/20 10:30 AM, Chris Down wrote:
>>Astractly, I think this really overcomplicates the API a lot. If
>>these are truly generally useful (and I think that remains to be
>>demonstrated), they should be additions to the existing API, rather
>>than a sidestep with prctl.
>This patchset is derived from customer requests. With existing API, I
>suppose you mean the memory cgroup API. Right? The reason to use
>prctl() is that there are users out there who want some kind of
>per-process control instead of for a whole group of processes unless
>the users try to create one cgroup per process which is not very
>efficient.
If using one cgroup per process is inefficient, then that's what needs to be
fixed. Making the API extremely complex to reason about for every user isn't a
good compromise when we're talking about an already niche use case.
>>I also worry about some other more concrete things:
>>
>>1. Doesn't this allow unprivileged applications to potentially
>>bypass memory.high constraints set by a system administrator?
>The memory.high constraint is for triggering memory reclaim. The new
>mitigation actions introduced by this patchset will only be applied if
>memory reclaim alone fails to limit the physical memory consumption.
>The current memory cgroup memory reclaim code will not be affected by
>this patchset.
memory.high isn't only for triggering memory reclaim, it's also about active
throttling when the application fails to come under. Fundamentally it's
supposed to indicate the point at which we expect the application to either
cooperate or get forcibly descheduled -- take a look at where we call
schedule_timeout_killable.
I really struggle to think about how all of those things should interact in
this patchset.
>>2. What's the purpose of PR_MEMACT_KILL, compared to memory.max?
>A user can use this to specify which processes are less important and
>can be sacrificed first instead of the other more important ones in
>case they are really in a OOM situation. IOW, users can specify the
>order where OOM kills can happen.
You can already do that with something like oomd, which has way more
flexibility than this. Why codify this in the kernel instead of in a userspace
agent?
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [RFC PATCH 1/8] memcg: Enable fine-grained control of over memory.high action
2020-08-17 14:08 ` [RFC PATCH 1/8] memcg: Enable fine-grained control of over memory.high action Waiman Long
2020-08-17 14:30 ` Chris Down
@ 2020-08-17 16:44 ` Shakeel Butt
2020-08-17 16:56 ` Chris Down
2020-08-18 19:14 ` Waiman Long
1 sibling, 2 replies; 42+ messages in thread
From: Shakeel Butt @ 2020-08-17 16:44 UTC (permalink / raw)
To: Waiman Long
Cc: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot, LKML, linux-doc, linux-fsdevel,
Cgroups, Linux MM
On Mon, Aug 17, 2020 at 7:11 AM Waiman Long <longman@redhat.com> wrote:
>
> Memory controller can be used to control and limit the amount of
> physical memory used by a task. When a limit is set in "memory.high"
> in a non-root memory cgroup, the memory controller will try to reclaim
> memory if the limit has been exceeded. Normally, that will be enough
> to keep the physical memory consumption of tasks in the memory cgroup
> to be around or below the "memory.high" limit.
>
> Sometimes, memory reclaim may not be able to recover memory in a rate
> that can catch up to the physical memory allocation rate especially
> when rotating disks are used for swapping or writing dirty pages. In
> this case, the physical memory consumption will keep on increasing.
Isn't this the real underlying issue? Why not make the guarantees of
memory.high more strict instead of adding more interfaces and
complexity?
By the way, have you observed this issue on real workloads or some
test cases? It would be good to get a repro with simple test cases.
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [RFC PATCH 1/8] memcg: Enable fine-grained control of over memory.high action
2020-08-17 16:44 ` Shakeel Butt
@ 2020-08-17 16:56 ` Chris Down
2020-08-18 19:12 ` Waiman Long
2020-08-18 19:14 ` Waiman Long
1 sibling, 1 reply; 42+ messages in thread
From: Chris Down @ 2020-08-17 16:56 UTC (permalink / raw)
To: Shakeel Butt
Cc: Waiman Long, Andrew Morton, Johannes Weiner, Michal Hocko,
Vladimir Davydov, Jonathan Corbet, Alexey Dobriyan, Ingo Molnar,
Peter Zijlstra, Juri Lelli, Vincent Guittot, LKML, linux-doc,
linux-fsdevel, Cgroups, Linux MM
Shakeel Butt writes:
>> Sometimes, memory reclaim may not be able to recover memory in a rate
>> that can catch up to the physical memory allocation rate especially
>> when rotating disks are used for swapping or writing dirty pages. In
>> this case, the physical memory consumption will keep on increasing.
>
>Isn't this the real underlying issue? Why not make the guarantees of
>memory.high more strict instead of adding more interfaces and
>complexity?
Oh, thanks Shakeel for bringing this up. I missed this in the original
changelog and I'm surprised that it's mentioned, since we do have protections
against that.
Waiman, we already added artificial throttling if memory reclaim is not
sufficiently achieved in 0e4b01df8659 ("mm, memcg: throttle allocators when
failing reclaim over memory.high"), which has been present since v5.4. This
should significantly inhibit physical memory consumption from increasing. What
problems are you having with that? :-)
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 1/8] memcg: Enable fine-grained control of over memory.high action
2020-08-17 16:56 ` Chris Down
@ 2020-08-18 19:12 ` Waiman Long
0 siblings, 0 replies; 42+ messages in thread
From: Waiman Long @ 2020-08-18 19:12 UTC (permalink / raw)
To: Chris Down, Shakeel Butt
Cc: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot, LKML, linux-doc, linux-fsdevel,
Cgroups, Linux MM
On 8/17/20 12:56 PM, Chris Down wrote:
> Shakeel Butt writes:
>>> Sometimes, memory reclaim may not be able to recover memory in a rate
>>> that can catch up to the physical memory allocation rate especially
>>> when rotating disks are used for swapping or writing dirty pages. In
>>> this case, the physical memory consumption will keep on increasing.
>>
>> Isn't this the real underlying issue? Why not make the guarantees of
>> memory.high more strict instead of adding more interfaces and
>> complexity?
>
> Oh, thanks Shakeel for bringing this up. I missed this in the original
> changelog and I'm surprised that it's mentioned, since we do have
> protections against that.
>
> Waiman, we already added artificial throttling if memory reclaim is
> not sufficiently achieved in 0e4b01df8659 ("mm, memcg: throttle
> allocators when failing reclaim over memory.high"), which has been
> present since v5.4. This should significantly inhibit physical memory
> consumption from increasing. What problems are you having with that? :-)
>
Oh, I think I overlooked your patch. You are right. There are already
throttling in place. So I need to re-examine my patch to see if it is
still necessary or reduce the scope of the patch.
Thanks,
Longman
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [RFC PATCH 1/8] memcg: Enable fine-grained control of over memory.high action
2020-08-17 16:44 ` Shakeel Butt
2020-08-17 16:56 ` Chris Down
@ 2020-08-18 19:14 ` Waiman Long
1 sibling, 0 replies; 42+ messages in thread
From: Waiman Long @ 2020-08-18 19:14 UTC (permalink / raw)
To: Shakeel Butt
Cc: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot, LKML, linux-doc, linux-fsdevel,
Cgroups, Linux MM
On 8/17/20 12:44 PM, Shakeel Butt wrote:
> On Mon, Aug 17, 2020 at 7:11 AM Waiman Long <longman@redhat.com> wrote:
>> Memory controller can be used to control and limit the amount of
>> physical memory used by a task. When a limit is set in "memory.high"
>> in a non-root memory cgroup, the memory controller will try to reclaim
>> memory if the limit has been exceeded. Normally, that will be enough
>> to keep the physical memory consumption of tasks in the memory cgroup
>> to be around or below the "memory.high" limit.
>>
>> Sometimes, memory reclaim may not be able to recover memory in a rate
>> that can catch up to the physical memory allocation rate especially
>> when rotating disks are used for swapping or writing dirty pages. In
>> this case, the physical memory consumption will keep on increasing.
> Isn't this the real underlying issue? Why not make the guarantees of
> memory.high more strict instead of adding more interfaces and
> complexity?
>
> By the way, have you observed this issue on real workloads or some
> test cases? It would be good to get a repro with simple test cases.
>
As said before, this is from a customer request. I will need to
re-examine the existing features to see if they can satisfy the customer
need.
Cheers,
Longman
^ permalink raw reply [flat|nested] 42+ messages in thread
* [RFC PATCH 2/8] memcg, mm: Return ENOMEM or delay if memcg_over_limit
2020-08-17 14:08 [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control Waiman Long
2020-08-17 14:08 ` [RFC PATCH 1/8] memcg: Enable fine-grained control of over memory.high action Waiman Long
@ 2020-08-17 14:08 ` Waiman Long
2020-08-17 14:08 ` [RFC PATCH 4/8] fs/proc: Support a new procfs memctl file Waiman Long
` (5 subsequent siblings)
7 siblings, 0 replies; 42+ messages in thread
From: Waiman Long @ 2020-08-17 14:08 UTC (permalink / raw)
To: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot
Cc: linux-kernel, linux-doc, linux-fsdevel, cgroups, linux-mm,
Waiman Long
The brk(), mmap(), mlock(), mlockall() and mprotect() syscalls are
modified to check the memcg_over_limit flag and return ENOMEM when it
is set and memory control action is PR_MEMACT_ENOMEM.
In case the action is PR_MEMACT_SLOWDOWN, an artificial delay of 20ms
will be added to slow down the memory allocation syscalls.
Signed-off-by: Waiman Long <longman@redhat.com>
---
include/linux/sched.h | 16 ++++++++++++++++
kernel/fork.c | 1 +
mm/memcontrol.c | 25 +++++++++++++++++++++++--
mm/mlock.c | 6 ++++++
mm/mmap.c | 12 ++++++++++++
mm/mprotect.c | 3 +++
6 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index c79d606d27ab..9ec1bd072334 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1477,6 +1477,22 @@ static inline char task_state_to_char(struct task_struct *tsk)
return task_index_to_char(task_state_index(tsk));
}
+#ifdef CONFIG_MEMCG
+extern bool mem_cgroup_check_over_limit(void);
+
+static inline bool mem_over_memcg_limit(void)
+{
+ if (READ_ONCE(current->memcg_over_limit))
+ return mem_cgroup_check_over_limit();
+ return false;
+}
+#else
+static inline bool mem_over_memcg_limit(void)
+{
+ return false;
+}
+#endif
+
/**
* is_global_init - check if a task structure is init. Since init
* is free to have sub-threads we need to check tgid.
diff --git a/kernel/fork.c b/kernel/fork.c
index 4d32190861bd..61f9a9e5f857 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -940,6 +940,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
#ifdef CONFIG_MEMCG
tsk->active_memcg = NULL;
+ tsk->memcg_over_limit = false;
#endif
return tsk;
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 1106dac024ac..5cad7bb26d13 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2646,7 +2646,9 @@ static bool __mem_cgroup_over_high_action(struct mem_cgroup *memcg, u8 action)
if (!mm)
return true; /* No more check is needed */
- current->memcg_over_limit = false;
+ if (READ_ONCE(current->memcg_over_limit))
+ WRITE_ONCE(current->memcg_over_limit, false);
+
if ((action == PR_MEMACT_SIGNAL) && !signal)
goto out;
@@ -2660,7 +2662,11 @@ static bool __mem_cgroup_over_high_action(struct mem_cgroup *memcg, u8 action)
WRITE_ONCE(current->memcg_over_limit, true);
break;
case PR_MEMACT_SLOWDOWN:
- /* Slow down by yielding the cpu */
+ /*
+ * Slow down by yielding the cpu & adding delay to
+ * memory allocation syscalls.
+ */
+ WRITE_ONCE(current->memcg_over_limit, true);
set_tsk_need_resched(current);
set_preempt_need_resched();
break;
@@ -2694,6 +2700,21 @@ static inline bool mem_cgroup_over_high_action(struct mem_cgroup *memcg)
return __mem_cgroup_over_high_action(memcg, action);
}
+/*
+ * Called from memory allocation syscalls.
+ * Return true if ENOMEM should be returned, false otherwise.
+ */
+bool mem_cgroup_check_over_limit(void)
+{
+ u8 action = READ_ONCE(current->memcg_over_high_action);
+
+ if (action == PR_MEMACT_ENOMEM)
+ return true;
+ if (action == PR_MEMACT_SLOWDOWN)
+ msleep(20); /* Artificial delay of 20ms */
+ return false;
+}
+
static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask,
unsigned int nr_pages)
{
diff --git a/mm/mlock.c b/mm/mlock.c
index 93ca2bf30b4f..130d4b3fa0f5 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -678,6 +678,9 @@ static __must_check int do_mlock(unsigned long start, size_t len, vm_flags_t fla
if (!can_do_mlock())
return -EPERM;
+ if (mem_over_memcg_limit())
+ return -ENOMEM;
+
len = PAGE_ALIGN(len + (offset_in_page(start)));
start &= PAGE_MASK;
@@ -807,6 +810,9 @@ SYSCALL_DEFINE1(mlockall, int, flags)
if (!can_do_mlock())
return -EPERM;
+ if (mem_over_memcg_limit())
+ return -ENOMEM;
+
lock_limit = rlimit(RLIMIT_MEMLOCK);
lock_limit >>= PAGE_SHIFT;
diff --git a/mm/mmap.c b/mm/mmap.c
index 40248d84ad5f..873ccf2560a6 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -198,6 +198,10 @@ SYSCALL_DEFINE1(brk, unsigned long, brk)
bool downgraded = false;
LIST_HEAD(uf);
+ /* Too much memory used? */
+ if (mem_over_memcg_limit())
+ return -ENOMEM;
+
if (mmap_write_lock_killable(mm))
return -EINTR;
@@ -1407,6 +1411,10 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
if (mm->map_count > sysctl_max_map_count)
return -ENOMEM;
+ /* Too much memory used? */
+ if (mem_over_memcg_limit())
+ return -ENOMEM;
+
/* Obtain the address to map to. we verify (or select) it and ensure
* that it represents a valid section of the address space.
*/
@@ -1557,6 +1565,10 @@ unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len,
struct file *file = NULL;
unsigned long retval;
+ /* Too much memory used? */
+ if (mem_over_memcg_limit())
+ return -ENOMEM;
+
if (!(flags & MAP_ANONYMOUS)) {
audit_mmap_fd(fd, flags);
file = fget(fd);
diff --git a/mm/mprotect.c b/mm/mprotect.c
index ce8b8a5eacbb..b2c0f50bb0a0 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -519,6 +519,9 @@ static int do_mprotect_pkey(unsigned long start, size_t len,
const bool rier = (current->personality & READ_IMPLIES_EXEC) &&
(prot & PROT_READ);
+ if (mem_over_memcg_limit())
+ return -ENOMEM;
+
start = untagged_addr(start);
prot &= ~(PROT_GROWSDOWN|PROT_GROWSUP);
--
2.18.1
^ permalink raw reply related [flat|nested] 42+ messages in thread* [RFC PATCH 4/8] fs/proc: Support a new procfs memctl file
2020-08-17 14:08 [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control Waiman Long
2020-08-17 14:08 ` [RFC PATCH 1/8] memcg: Enable fine-grained control of over memory.high action Waiman Long
2020-08-17 14:08 ` [RFC PATCH 2/8] memcg, mm: Return ENOMEM or delay if memcg_over_limit Waiman Long
@ 2020-08-17 14:08 ` Waiman Long
2020-08-17 14:08 ` [RFC PATCH 5/8] memcg: Allow direct per-task memory limit checking Waiman Long
` (4 subsequent siblings)
7 siblings, 0 replies; 42+ messages in thread
From: Waiman Long @ 2020-08-17 14:08 UTC (permalink / raw)
To: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot
Cc: linux-kernel, linux-doc, linux-fsdevel, cgroups, linux-mm,
Waiman Long
To allow system administrators to view and modify the over-high action
settings of a running application, a new /proc/<pid>/memctl file is
now added to show the over-high action parameters as well as allowing
their modification.
Signed-off-by: Waiman Long <longman@redhat.com>
---
fs/proc/base.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 109 insertions(+)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 617db4e0faa0..3c9349ad1e37 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -88,6 +88,8 @@
#include <linux/user_namespace.h>
#include <linux/fs_struct.h>
#include <linux/slab.h>
+#include <linux/prctl.h>
+#include <linux/ctype.h>
#include <linux/sched/autogroup.h>
#include <linux/sched/mm.h>
#include <linux/sched/coredump.h>
@@ -3145,6 +3147,107 @@ static int proc_stack_depth(struct seq_file *m, struct pid_namespace *ns,
}
#endif /* CONFIG_STACKLEAK_METRICS */
+#ifdef CONFIG_MEMCG
+/*
+ * Memory cgroup control parameters
+ * <over_high_action> <limit1> <limit2>
+ */
+static ssize_t proc_memctl_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct task_struct *task = get_proc_task(file_inode(file));
+ unsigned long action, limit1, limit2;
+ char buffer[80];
+ ssize_t len;
+
+ if (!task)
+ return -ESRCH;
+
+ action = task->memcg_over_high_action |
+ (task->memcg_over_high_signal << PR_MEMACT_SIG_SHIFT) |
+ (task->memcg_over_high_flags << PR_MEMACT_FLG_SHIFT);
+ limit1 = (unsigned long)task->memcg_over_high_climit * PAGE_SIZE;
+ limit2 = (unsigned long)task->memcg_over_high_plimit * PAGE_SIZE;
+
+ put_task_struct(task);
+ len = snprintf(buffer, sizeof(buffer), "%ld %ld %ld\n",
+ action, limit1, limit2);
+ return simple_read_from_buffer(buf, count, ppos, buffer, len);
+}
+
+static ssize_t proc_memctl_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *offs)
+{
+ struct task_struct *task = get_proc_task(file_inode(file));
+ unsigned long vals[3];
+ char buffer[80];
+ char *ptr, *next;
+ int i, err;
+ unsigned int action, signal, flags;
+
+ if (!task)
+ return -ESRCH;
+ if (count > sizeof(buffer) - 1)
+ count = sizeof(buffer) - 1;
+ if (copy_from_user(buffer, buf, count)) {
+ err = -EFAULT;
+ goto out;
+ }
+ buffer[count] = '\0';
+ next = buffer;
+
+ /*
+ * Expect to find 3 numbers
+ */
+ for (i = 0, ptr = buffer; i < 3; i++) {
+ ptr = skip_spaces(next);
+ if (!*ptr) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Skip non-space characters for next */
+ for (next = ptr; *next && !isspace(*next); next++)
+ ;
+ if (isspace(*next))
+ *next++ = '\0';
+
+ err = kstrtoul(ptr, 0, &vals[i]);
+ if (err)
+ break;
+ }
+ action = vals[0] & PR_MEMACT_MASK;
+ signal = (vals[0] >> PR_MEMACT_SIG_SHIFT) & PR_MEMACT_MASK;
+ flags = vals[0] >> PR_MEMACT_FLG_SHIFT;
+
+ /* Round up limits to number of pages */
+ vals[1] = DIV_ROUND_UP(vals[1], PAGE_SIZE);
+ vals[2] = DIV_ROUND_UP(vals[2], PAGE_SIZE);
+
+ /* Check input values */
+ if ((action > PR_MEMACT_MAX) || (signal >= _NSIG) ||
+ (flags & ~PR_MEMFLAG_MASK)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ WRITE_ONCE(task->memcg_over_high_action, action);
+ WRITE_ONCE(task->memcg_over_high_signal, signal);
+ WRITE_ONCE(task->memcg_over_high_flags, flags);
+ WRITE_ONCE(task->memcg_over_high_climit, vals[1]);
+ WRITE_ONCE(task->memcg_over_high_plimit, vals[2]);
+out:
+ put_task_struct(task);
+ return err < 0 ? err : count;
+}
+
+const struct file_operations proc_memctl_operations = {
+ .read = proc_memctl_read,
+ .write = proc_memctl_write,
+ .llseek = generic_file_llseek,
+};
+#endif /* CONFIG_MEMCG */
+
/*
* Thread groups
*/
@@ -3258,6 +3361,9 @@ static const struct pid_entry tgid_base_stuff[] = {
#ifdef CONFIG_PROC_PID_ARCH_STATUS
ONE("arch_status", S_IRUGO, proc_pid_arch_status),
#endif
+#ifdef CONFIG_MEMCG
+ REG("memctl", 0644, proc_memctl_operations),
+#endif
};
static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx)
@@ -3587,6 +3693,9 @@ static const struct pid_entry tid_base_stuff[] = {
#ifdef CONFIG_PROC_PID_ARCH_STATUS
ONE("arch_status", S_IRUGO, proc_pid_arch_status),
#endif
+#ifdef CONFIG_MEMCG
+ REG("memctl", 0644, proc_memctl_operations),
+#endif
};
static int proc_tid_base_readdir(struct file *file, struct dir_context *ctx)
--
2.18.1
^ permalink raw reply related [flat|nested] 42+ messages in thread* [RFC PATCH 5/8] memcg: Allow direct per-task memory limit checking
2020-08-17 14:08 [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control Waiman Long
` (2 preceding siblings ...)
2020-08-17 14:08 ` [RFC PATCH 4/8] fs/proc: Support a new procfs memctl file Waiman Long
@ 2020-08-17 14:08 ` Waiman Long
2020-08-17 14:08 ` [RFC PATCH 6/8] memcg: Introduce additional memory control slowdown if needed Waiman Long
` (3 subsequent siblings)
7 siblings, 0 replies; 42+ messages in thread
From: Waiman Long @ 2020-08-17 14:08 UTC (permalink / raw)
To: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot
Cc: linux-kernel, linux-doc, linux-fsdevel, cgroups, linux-mm,
Waiman Long
Up to now, the PR_SET_MEMCONTROL prctl(2) call enables user-specified
action only if the total memory consumption in the memory cgroup
exceeds memory.high by the additional memory threshold specified.
There are cases where a user may want direct memory consumption control
for certain applications even if the total cgroup memory consumption
has not exceeded the limit yet. One way of doing that is to create one
memory cgroup per application. However, if an application call other
helper applications, these helper applications will fall into the same
cgroup breaking the one application per cgroup rule.
Another alternative is to enable user to enable direct per-task memory
limit checking which is what this patch is about. That is for special
use cases and is not recommended for general use as memory reclaim may
not be triggered even if the per-task memory limit has been exceeded.
Signed-off-by: Waiman Long <longman@redhat.com>
---
include/uapi/linux/prctl.h | 4 ++-
mm/memcontrol.c | 52 +++++++++++++++++++++++++++-----------
2 files changed, 40 insertions(+), 16 deletions(-)
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index ef8d84c94b4a..7ba40e10737d 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -265,13 +265,15 @@ struct prctl_mm_map {
/* Flags for PR_SET_MEMCONTROL */
# define PR_MEMFLAG_SIGCONT (1UL << 0) /* Continuous signal delivery */
+# define PR_MEMFLAG_DIRECT (1UL << 1) /* Direct memory limit */
# define PR_MEMFLAG_RSS_ANON (1UL << 8) /* Check anonymous pages */
# define PR_MEMFLAG_RSS_FILE (1UL << 9) /* Check file pages */
# define PR_MEMFLAG_RSS_SHMEM (1UL << 10) /* Check shmem pages */
# define PR_MEMFLAG_RSS (PR_MEMFLAG_RSS_ANON |\
PR_MEMFLAG_RSS_FILE |\
PR_MEMFLAG_RSS_SHMEM)
-# define PR_MEMFLAG_MASK (PR_MEMFLAG_SIGCONT | PR_MEMFLAG_RSS)
+# define PR_MEMFLAG_MASK (PR_MEMFLAG_SIGCONT | PR_MEMFLAG_RSS |\
+ PR_MEMFLAG_DIRECT)
/* Action word masks */
# define PR_MEMACT_MASK 0xff
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index aa76bae7f408..6488f8a10d66 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2640,27 +2640,27 @@ get_rss_counter(struct mm_struct *mm, int mm_bit, u16 flags, int rss_bit)
* Return true if an action has been taken or further check is not needed,
* false otherwise.
*/
-static bool __mem_cgroup_over_high_action(struct mem_cgroup *memcg, u8 action)
+static bool __mem_cgroup_over_high_action(struct mem_cgroup *memcg, u8 action,
+ u16 flags)
{
- unsigned long mem;
+ unsigned long mem = 0;
bool ret = false;
struct mm_struct *mm = get_task_mm(current);
u8 signal = READ_ONCE(current->memcg_over_high_signal);
- u16 flags = READ_ONCE(current->memcg_over_high_flags);
- u32 limit = READ_ONCE(current->memcg_over_high_climit);
+ u32 limit;
if (!mm)
return true; /* No more check is needed */
- if (READ_ONCE(current->memcg_over_limit))
- WRITE_ONCE(current->memcg_over_limit, false);
-
if ((action == PR_MEMACT_SIGNAL) && !signal)
goto out;
- mem = page_counter_read(&memcg->memory);
- if (mem <= memcg->memory.high + limit)
- goto out;
+ if (memcg) {
+ mem = page_counter_read(&memcg->memory);
+ limit = READ_ONCE(current->memcg_over_high_climit);
+ if (mem <= memcg->memory.high + limit)
+ goto out;
+ }
/*
* Check RSS memory if any of the PR_MEMFLAG_RSS flags is set.
@@ -2706,20 +2706,34 @@ static bool __mem_cgroup_over_high_action(struct mem_cgroup *memcg, u8 action)
out:
mmput(mm);
- return ret;
+ /*
+ * We only need to do direct per-task memory limit checking once.
+ */
+ return memcg ? ret : true;
}
/*
* Return true if an action has been taken or further check is not needed,
* false otherwise.
*/
-static inline bool mem_cgroup_over_high_action(struct mem_cgroup *memcg)
+static inline bool mem_cgroup_over_high_action(struct mem_cgroup *memcg,
+ bool mem_high)
{
u8 action = READ_ONCE(current->memcg_over_high_action);
+ u16 flags = READ_ONCE(current->memcg_over_high_flags);
if (!action)
return true; /* No more check is needed */
- return __mem_cgroup_over_high_action(memcg, action);
+
+ if (READ_ONCE(current->memcg_over_limit))
+ WRITE_ONCE(current->memcg_over_limit, false);
+
+ if (flags & PR_MEMFLAG_DIRECT)
+ memcg = NULL; /* Direct per-task memory limit checking */
+ else if (!mem_high)
+ return false;
+
+ return __mem_cgroup_over_high_action(memcg, action, flags);
}
/*
@@ -2907,8 +2921,8 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask,
swap_high = page_counter_read(&memcg->swap) >
READ_ONCE(memcg->swap.high);
- if (mem_high && !taken)
- taken = mem_cgroup_over_high_action(memcg);
+ if (!taken)
+ taken = mem_cgroup_over_high_action(memcg, mem_high);
/* Don't bother a random interrupted task */
if (in_interrupt()) {
@@ -7103,6 +7117,14 @@ long mem_cgroup_over_high_set(struct task_struct *task, unsigned long action,
(sig >= _NSIG))
return -EINVAL;
+ /*
+ * PR_MEMFLAG_DIRECT can only be set if any of the PR_MEMFLAG_RSS flag
+ * is set and limit2 is non-zero.
+ */
+ if ((flags & PR_MEMFLAG_DIRECT) &&
+ (!(flags & PR_MEMFLAG_RSS) || !limit2))
+ return -EINVAL;
+
WRITE_ONCE(task->memcg_over_high_action, cmd);
WRITE_ONCE(task->memcg_over_high_signal, sig);
WRITE_ONCE(task->memcg_over_high_flags, flags);
--
2.18.1
^ permalink raw reply related [flat|nested] 42+ messages in thread* [RFC PATCH 6/8] memcg: Introduce additional memory control slowdown if needed
2020-08-17 14:08 [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control Waiman Long
` (3 preceding siblings ...)
2020-08-17 14:08 ` [RFC PATCH 5/8] memcg: Allow direct per-task memory limit checking Waiman Long
@ 2020-08-17 14:08 ` Waiman Long
2020-08-17 14:08 ` [RFC PATCH 8/8] memcg: Add over-high action prctl() documentation Waiman Long
` (2 subsequent siblings)
7 siblings, 0 replies; 42+ messages in thread
From: Waiman Long @ 2020-08-17 14:08 UTC (permalink / raw)
To: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot
Cc: linux-kernel, linux-doc, linux-fsdevel, cgroups, linux-mm,
Waiman Long
For fast cpus on slow disks, yielding the cpus repeatedly with
PR_MEMACT_SLOWDOWN may not be able to slow down memory allocation enough
for memory reclaim to catch up. In case a large memory block is mmap'ed
and the pages are faulted in one-by-one, the syscall delays won't be
activated during this process.
To be safe, an additional variable delay of 20-5000 us will be added
to __mem_cgroup_over_high_action() if the excess memory used is more
than 1/256 of the memory limit.
Signed-off-by: Waiman Long <longman@redhat.com>
---
mm/memcontrol.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 6488f8a10d66..bddf3e659469 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2643,11 +2643,10 @@ get_rss_counter(struct mm_struct *mm, int mm_bit, u16 flags, int rss_bit)
static bool __mem_cgroup_over_high_action(struct mem_cgroup *memcg, u8 action,
u16 flags)
{
- unsigned long mem = 0;
+ unsigned long mem = 0, limit = 0, excess = 0;
bool ret = false;
struct mm_struct *mm = get_task_mm(current);
u8 signal = READ_ONCE(current->memcg_over_high_signal);
- u32 limit;
if (!mm)
return true; /* No more check is needed */
@@ -2657,9 +2656,10 @@ static bool __mem_cgroup_over_high_action(struct mem_cgroup *memcg, u8 action,
if (memcg) {
mem = page_counter_read(&memcg->memory);
- limit = READ_ONCE(current->memcg_over_high_climit);
- if (mem <= memcg->memory.high + limit)
+ limit = READ_ONCE(current->memcg_over_high_climit) + memcg->memory.high;
+ if (mem <= limit)
goto out;
+ excess = mem - limit;
}
/*
@@ -2676,6 +2676,7 @@ static bool __mem_cgroup_over_high_action(struct mem_cgroup *memcg, u8 action,
limit = READ_ONCE(current->memcg_over_high_plimit);
if (mem <= limit)
goto out;
+ excess = mem - limit;
}
ret = true;
@@ -2685,10 +2686,19 @@ static bool __mem_cgroup_over_high_action(struct mem_cgroup *memcg, u8 action,
break;
case PR_MEMACT_SLOWDOWN:
/*
- * Slow down by yielding the cpu & adding delay to
- * memory allocation syscalls.
+ * Slow down by yielding the cpu & adding delay to memory
+ * allocation syscalls.
+ *
+ * An additional 20-5000 us of delay is added in case the
+ * excess memory is more than 1/256 of the limit.
*/
WRITE_ONCE(current->memcg_over_limit, true);
+ limit >>= 8;
+ if (limit && (excess > limit)) {
+ int delay = min(5000UL, excess/limit * 20UL);
+
+ udelay(delay);
+ }
set_tsk_need_resched(current);
set_preempt_need_resched();
break;
--
2.18.1
^ permalink raw reply related [flat|nested] 42+ messages in thread* [RFC PATCH 8/8] memcg: Add over-high action prctl() documentation
2020-08-17 14:08 [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control Waiman Long
` (4 preceding siblings ...)
2020-08-17 14:08 ` [RFC PATCH 6/8] memcg: Introduce additional memory control slowdown if needed Waiman Long
@ 2020-08-17 14:08 ` Waiman Long
2020-08-17 15:26 ` [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control Michal Hocko
[not found] ` <20200817140831.30260-1-longman-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
7 siblings, 0 replies; 42+ messages in thread
From: Waiman Long @ 2020-08-17 14:08 UTC (permalink / raw)
To: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot
Cc: linux-kernel, linux-doc, linux-fsdevel, cgroups, linux-mm,
Waiman Long
A new memcontrol.rst documentation file is added to document the new
prctl(2) interface for setting the over-high mitigation action parameters
and retrieving them.
Signed-off-by: Waiman Long <longman@redhat.com>
---
Documentation/userspace-api/index.rst | 1 +
Documentation/userspace-api/memcontrol.rst | 174 +++++++++++++++++++++
2 files changed, 175 insertions(+)
create mode 100644 Documentation/userspace-api/memcontrol.rst
diff --git a/Documentation/userspace-api/index.rst b/Documentation/userspace-api/index.rst
index 69fc5167e648..1c0fc7a7f4ec 100644
--- a/Documentation/userspace-api/index.rst
+++ b/Documentation/userspace-api/index.rst
@@ -23,6 +23,7 @@ place where this information is gathered.
accelerators/ocxl
ioctl/index
media/index
+ memcontrol
.. only:: subproject and html
diff --git a/Documentation/userspace-api/memcontrol.rst b/Documentation/userspace-api/memcontrol.rst
new file mode 100644
index 000000000000..0cfcc72ad5f0
--- /dev/null
+++ b/Documentation/userspace-api/memcontrol.rst
@@ -0,0 +1,174 @@
+==============
+Memory Control
+==============
+
+Memory controller can be used to control and limit the amount of
+physical memory used by a task. When a limit is set in "memory.high" in
+a v2 non-root memory cgroup, the memory controller will try to reclaim
+memory if the limit has been exceeded. Normally, that will be enough
+to keep the physical memory consumption of tasks in the memory cgroup
+to be around or below the "memory.high" limit.
+
+Sometimes, memory reclaim may not be able to recover memory in a rate
+that can catch up to the physical memory allocation rate. In this case,
+the physical memory consumption will keep on increasing. For memory
+cgroup v2, when it is reaching "memory.max" or the system is running
+out of free memory, the OOM killer will be invoked to kill some tasks
+to free up additional memory. However, one has little control of which
+tasks are going to be killed by an OOM killer. Killing tasks that hold
+some important resources without freeing them first can create other
+system problems.
+
+Users who do not want the OOM killer to be invoked to kill random
+tasks in an out-of-memory situation can use the memory control facility
+provided by :manpage:`prctl(2)` to better manage the mitigation action
+that needs to be performed to an individual task when the specified
+memory limit is exceeded with memory cgroup v2 being used.
+
+The task to be controlled must be running in a non-root memory cgroup
+as no limit will be imposed on tasks running in the root memory cgroup.
+
+There are two prctl commands related to this:
+
+ * PR_SET_MEMCONTROL
+
+ * PR_GET_MEMCONTROL
+
+
+PR_SET_MEMCONTROL
+-----------------
+
+PR_SET_MEMCTROL controls what action should be taken when the memory
+limit is exceeded.
+
+The arg2 of :manpage:`prctl(2)` sets the desired mitigation action. The
+action code consists of three different parts:
+
+ * Bits 0-7: action command
+
+ * Bits 8-15: signal number
+
+ * Bits 16-31: flags
+
+The currently supported action commands are:
+
+====== ================== ================================================
+Value Define Description
+====== ================== ================================================
+0 PR_MEMACT_NONE Use the default memory cgroup behavior
+1 PR_MEMACT_ENOMEM Return ENOMEM for selected syscalls that try to
+ allocate more memory when the preset memory limit
+ is exceeded
+2 PR_MEMACT_SLOWDOWN Slow down the process for memory reclaim to
+ catch up when memory limit is exceeded
+3 PR_MEMACT_SIGNAL Send a signal to the task that has exceeded
+ preset memory limit
+4 PR_MEMACT_KILL Kill the task that has exceeded preset memory
+ limit
+====== ================== ================================================
+
+The currently supports flags are:
+
+====== ==================== ================================================
+Value Define Description
+====== ==================== ================================================
+0x01 PR_MEMFLAG_SIGCONT Send a signal on every allocation request instead
+ of a one-shot signal
+0x02 PR_MEMFLAG_DIRECT Check per-task memory limit irrespective of cgroup
+ setting
+0x04 PR_MEMFLAG_LOG Log any actions taken to the kernel ring buffer
+0x10 PR_MEMFLAG_RSS_ANON Check process anonymous memory
+0x20 PR_MEMFLAG_RSS_FILE Check process page caches
+0x40 PR_MEMFLAG_RSS_SHMEM Check process shared memory
+0x70 PR_MEMFLAG_RSS Equivalent to (PR_MEMFLAG_RSS_ANON |
+ PR_MEMFLAG_RSS_FILE | PR_MEMFLAG_RSS_SHMEM)
+====== ==================== ================================================
+
+If the action command is PR_MEMACT_SIGNAL, bits 16-23 of the action
+code contains the signal number to be used when the memory limit is
+exceeded. By default, the signal number is reset after delivery so
+that the signal will be delivered only once. Another PR_SET_MEMCONTROL
+command will have to be issued to set the signal again. If the user
+want a non-fatal signal to be delivered every time when the memory
+limit is breached without doing another PR_SET_MEMCONTROL call, the
+PR_MEMFLAG_SIGCONT flag can be set.
+
+The arg3 of :manpage:`prctl(2)` sets the additional memory cgroup
+limit that will be added to the value specified in the "memory.high"
+control file to get the real limit over which action specified in the
+action command will be triggered. This is to make sure that mitigation
+action will only be taken when the kernel memory reclaim facility fails
+to limit the growth of physical memory usage.
+
+If any of the PR_MEMFLAG_RSS* flag is specified, arg4 contains the
+per-process memory limit that will be used to compare against the sum
+of the specified RSS memory consumption of the process to determine
+if action will be taken provided that overall memory consumption has
+exceeded the "memory.high" + arg3 limit when the PR_MEMFLAG_DIRECT flag
+isn't set.
+
+If the PR_MEMFLAG_DIRECT flag is set, however, the cgroup memory limit
+is ignored and a memory-over-limit check will be performed on each
+memory allocation request, if applicable. This is reserved for special
+use case and is not recommended for general use.
+
+
+PR_GET_MEMCONTROL
+-----------------
+
+PR_GET_MEMCONTROL returns the parameters set by a previous
+PR_SET_MEMCONTROL command.
+
+The arg2 of :manpage:`prctl(2)` sets type of parameter that is to be
+returned. The possible values are:
+
+====== =================== ================================================
+Value Define Description
+====== =================== ================================================
+0 PR_MEMGET_ACTION Return the action code - command, flags & signal
+1 PR_MEMGET_CLIMIT Return the additional cgroup memory limit (in bytes)
+2 PR_MEMGET_PLIMIT Return the process memory limit for PR_MEMFLAG_RSS*
+====== =================== ================================================
+
+
+/proc/<pid>/memctl
+------------------
+
+PR_GET_MEMCONTROL only returns memory control setting about the
+task itself. To find those information about other tasks, the
+/proc/<pid>/memctl file can be read. This file reports three integer
+parameters:
+
+ * action code
+
+ * cgroup additional memory limit
+
+ * process memory limit for PR_MEMFLAG_RSS* flags
+
+These are the same values that will be returned if the task is
+calling :manpage:`prctl(2)` with PR_GET_MEMCONTROL command and the
+PR_MEMGET_ACTION, PR_MEMGET_CLIMIT and PR_MEMGET_PLIMIT arguments
+respectively.
+
+Privileged users can also write to the memctl file directly to modify
+those parameters for a given task.
+
+This procfs file is present for each of the running threads of a process.
+So multiple writes to each of them are needed to update the parameters
+for all the threads within a running process.
+
+Affected Syscalls
+-----------------
+
+The following system calls have additional check for the over-high
+memory usage flag that is set by the above memory control facility.
+
+ * :manpage:`brk(2)`
+
+ * :manpage:`mlock(2)`
+
+ * :manpage:`mlock2(2)`
+
+ * :manpage:`mlockall(2)`
+
+ * :manpage:`mmap(2)`
--
2.18.1
^ permalink raw reply related [flat|nested] 42+ messages in thread* Re: [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control
2020-08-17 14:08 [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control Waiman Long
` (5 preceding siblings ...)
2020-08-17 14:08 ` [RFC PATCH 8/8] memcg: Add over-high action prctl() documentation Waiman Long
@ 2020-08-17 15:26 ` Michal Hocko
[not found] ` <20200817152655.GE28270-2MMpYkNvuYDjFM9bn6wA6Q@public.gmane.org>
[not found] ` <20200817140831.30260-1-longman-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
7 siblings, 1 reply; 42+ messages in thread
From: Michal Hocko @ 2020-08-17 15:26 UTC (permalink / raw)
To: Waiman Long
Cc: Andrew Morton, Johannes Weiner, Vladimir Davydov, Jonathan Corbet,
Alexey Dobriyan, Ingo Molnar, Peter Zijlstra, Juri Lelli,
Vincent Guittot, linux-kernel, linux-doc, linux-fsdevel, cgroups,
linux-mm
On Mon 17-08-20 10:08:23, Waiman Long wrote:
> Memory controller can be used to control and limit the amount of
> physical memory used by a task. When a limit is set in "memory.high" in
> a v2 non-root memory cgroup, the memory controller will try to reclaim
> memory if the limit has been exceeded. Normally, that will be enough
> to keep the physical memory consumption of tasks in the memory cgroup
> to be around or below the "memory.high" limit.
>
> Sometimes, memory reclaim may not be able to recover memory in a rate
> that can catch up to the physical memory allocation rate. In this case,
> the physical memory consumption will keep on increasing. When it reaches
> "memory.max" for memory cgroup v2 or when the system is running out of
> free memory, the OOM killer will be invoked to kill some tasks to free
> up additional memory. However, one has little control of which tasks
> are going to be killed by an OOM killer. Killing tasks that hold some
> important resources without freeing them first can create other system
> problems down the road.
>
> Users who do not want the OOM killer to be invoked to kill random
> tasks in an out-of-memory situation can use the memory control
> facility provided by this new patchset via prctl(2) to better manage
> the mitigation action that needs to be performed to various tasks when
> the specified memory limit is exceeded with memory cgroup v2 being used.
>
> The currently supported mitigation actions include the followings:
>
> 1) Return ENOMEM for some syscalls that allocate or handle memory
> 2) Slow down the process for memory reclaim to catch up
> 3) Send a specific signal to the task
> 4) Kill the task
>
> The users that want better memory control for their applicatons can
> either modify their applications to call the prctl(2) syscall directly
> with the new memory control command code or write the desired action to
> the newly provided memctl procfs files of their applications provided
> that those applications run in a non-root v2 memory cgroup.
prctl is fundamentally about per-process control while cgroup (not only
memcg) is about group of processes interface. How do those two interact
together? In other words what is the semantic when different processes
have a different views on the same underlying memcg event?
Also the above description doesn't really describe any usecase which
struggles with the existing interface. We already do allow slow down and
along with PSI also provide user space control over close to OOM
situation.
--
Michal Hocko
SUSE Labs
^ permalink raw reply [flat|nested] 42+ messages in thread[parent not found: <20200817140831.30260-1-longman-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>]
* [RFC PATCH 3/8] memcg: Allow the use of task RSS memory as over-high action trigger
[not found] ` <20200817140831.30260-1-longman-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
@ 2020-08-17 14:08 ` Waiman Long
2020-08-17 14:08 ` [RFC PATCH 7/8] memcg: Enable logging of memory control mitigation action Waiman Long
2020-08-18 9:14 ` [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control peterz-wEGCiKHe2LqWVfeAwA7xHQ
2 siblings, 0 replies; 42+ messages in thread
From: Waiman Long @ 2020-08-17 14:08 UTC (permalink / raw)
To: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
cgroups-u79uwXL29TY76Z2rM5mHXA, linux-mm-Bw31MaZKKs3YtjvyW6yDsg,
Waiman Long
The total memory consumption of a task as tracked by memory cgroup
includes different types of memory like page caches, anonymous memory,
share memory and kernel memory.
In a memory cgroup with a multiple running tasks, using total memory
consumption of all the tasks within the cgroup as action trigger may
not be fair to tasks that don't contribute to excessive memory usage.
Page cache memory can typically be shared between multiple tasks. It
is also not easy to pin kernel memory usage to a specific task. That
leaves a task's anonymous (RSS) memory usage as best proxy for a task's
contribution to total memory consumption within the memory cgroup.
So a new set of PR_MEMFLAG_RSS_* flags are added to enable the checking
of a task's real RSS memory footprint as a trigger to over-high action
provided that the total memory consumption of the cgroup has exceeded
memory.high + the additional memcg memory limit.
Signed-off-by: Waiman Long <longman-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
include/linux/memcontrol.h | 2 +-
include/linux/sched.h | 3 ++-
include/uapi/linux/prctl.h | 14 +++++++++++---
kernel/sys.c | 4 ++--
mm/memcontrol.c | 32 ++++++++++++++++++++++++++++++--
5 files changed, 46 insertions(+), 9 deletions(-)
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 40e6ceb8209b..562958cf79d8 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -447,7 +447,7 @@ void mem_cgroup_migrate(struct page *oldpage, struct page *newpage);
long mem_cgroup_over_high_get(struct task_struct *task, unsigned long item);
long mem_cgroup_over_high_set(struct task_struct *task, unsigned long action,
- unsigned long limit);
+ unsigned long limit, unsigned long limit2);
static struct mem_cgroup_per_node *
mem_cgroup_nodeinfo(struct mem_cgroup *memcg, int nid)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 9ec1bd072334..a1e9ac8b9b16 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1265,11 +1265,12 @@ struct task_struct {
/* Number of pages to reclaim on returning to userland: */
unsigned int memcg_nr_pages_over_high;
- /* Memory over-high action, flags, signal and limit */
+ /* Memory over-high action, flags, signal and limits */
unsigned char memcg_over_high_action;
unsigned char memcg_over_high_signal;
unsigned short memcg_over_high_flags;
unsigned int memcg_over_high_climit;
+ unsigned int memcg_over_high_plimit;
unsigned int memcg_over_limit;
/* Used by memcontrol for targeted memcg charge: */
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index 87970ae7b32c..ef8d84c94b4a 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -244,9 +244,10 @@ struct prctl_mm_map {
/*
* PR_SET_MEMCONTROL:
- * 2 parameters are passed:
+ * 3 parameters are passed:
* - Action word
* - Memory cgroup additional memory limit
+ * - Flag specific memory limit
*
* The action word consists of 3 bit fields:
* - Bits 0-7 : over-memory-limit action code
@@ -263,8 +264,14 @@ struct prctl_mm_map {
# define PR_MEMACT_MAX PR_MEMACT_KILL
/* Flags for PR_SET_MEMCONTROL */
-# define PR_MEMFLAG_SIGCONT (1UL << 0) /* Continuous signal delivery */
-# define PR_MEMFLAG_MASK PR_MEMFLAG_SIGCONT
+# define PR_MEMFLAG_SIGCONT (1UL << 0) /* Continuous signal delivery */
+# define PR_MEMFLAG_RSS_ANON (1UL << 8) /* Check anonymous pages */
+# define PR_MEMFLAG_RSS_FILE (1UL << 9) /* Check file pages */
+# define PR_MEMFLAG_RSS_SHMEM (1UL << 10) /* Check shmem pages */
+# define PR_MEMFLAG_RSS (PR_MEMFLAG_RSS_ANON |\
+ PR_MEMFLAG_RSS_FILE |\
+ PR_MEMFLAG_RSS_SHMEM)
+# define PR_MEMFLAG_MASK (PR_MEMFLAG_SIGCONT | PR_MEMFLAG_RSS)
/* Action word masks */
# define PR_MEMACT_MASK 0xff
@@ -274,5 +281,6 @@ struct prctl_mm_map {
/* Return specified value for PR_GET_MEMCONTROL */
# define PR_MEMGET_ACTION 0
# define PR_MEMGET_CLIMIT 1
+# define PR_MEMGET_PLIMIT 2
#endif /* _LINUX_PRCTL_H */
diff --git a/kernel/sys.c b/kernel/sys.c
index 644b86235d7f..272f82227c2d 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -2541,9 +2541,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
error = mem_cgroup_over_high_get(me, arg2);
break;
case PR_SET_MEMCONTROL:
- if (arg4 || arg5)
+ if (arg5)
return -EINVAL;
- error = mem_cgroup_over_high_set(me, arg2, arg3);
+ error = mem_cgroup_over_high_set(me, arg2, arg3, arg4);
break;
#endif
default:
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 5cad7bb26d13..aa76bae7f408 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2629,6 +2629,12 @@ void mem_cgroup_handle_over_high(void)
css_put(&memcg->css);
}
+static inline unsigned long
+get_rss_counter(struct mm_struct *mm, int mm_bit, u16 flags, int rss_bit)
+{
+ return (flags & rss_bit) ? get_mm_counter(mm, mm_bit) : 0;
+}
+
/*
* Task specific action when over the high limit.
* Return true if an action has been taken or further check is not needed,
@@ -2656,6 +2662,22 @@ static bool __mem_cgroup_over_high_action(struct mem_cgroup *memcg, u8 action)
if (mem <= memcg->memory.high + limit)
goto out;
+ /*
+ * Check RSS memory if any of the PR_MEMFLAG_RSS flags is set.
+ */
+ if (flags & PR_MEMFLAG_RSS) {
+ mem = get_rss_counter(mm, MM_ANONPAGES, flags,
+ PR_MEMFLAG_RSS_ANON) +
+ get_rss_counter(mm, MM_FILEPAGES, flags,
+ PR_MEMFLAG_RSS_FILE) +
+ get_rss_counter(mm, MM_SHMEMPAGES, flags,
+ PR_MEMFLAG_RSS_SHMEM);
+
+ limit = READ_ONCE(current->memcg_over_high_plimit);
+ if (mem <= limit)
+ goto out;
+ }
+
ret = true;
switch (action) {
case PR_MEMACT_ENOMEM:
@@ -7063,12 +7085,15 @@ long mem_cgroup_over_high_get(struct task_struct *task, unsigned long item)
case PR_MEMGET_CLIMIT:
return (long)task->memcg_over_high_climit * PAGE_SIZE;
+
+ case PR_MEMGET_PLIMIT:
+ return (long)task->memcg_over_high_plimit * PAGE_SIZE;
}
return -EINVAL;
}
long mem_cgroup_over_high_set(struct task_struct *task, unsigned long action,
- unsigned long limit)
+ unsigned long limit, unsigned long limit2)
{
unsigned char cmd = action & PR_MEMACT_MASK;
unsigned char sig = (action >> PR_MEMACT_SIG_SHIFT) & PR_MEMACT_MASK;
@@ -7084,12 +7109,15 @@ long mem_cgroup_over_high_set(struct task_struct *task, unsigned long action,
if (cmd == PR_MEMACT_NONE) {
WRITE_ONCE(task->memcg_over_high_climit, 0);
+ WRITE_ONCE(task->memcg_over_high_plimit, 0);
} else {
/*
* Convert limits to # of pages
*/
- limit = DIV_ROUND_UP(limit, PAGE_SIZE);
+ limit = DIV_ROUND_UP(limit, PAGE_SIZE);
+ limit2 = DIV_ROUND_UP(limit2, PAGE_SIZE);
WRITE_ONCE(task->memcg_over_high_climit, limit);
+ WRITE_ONCE(task->memcg_over_high_plimit, limit2);
}
return 0;
}
--
2.18.1
^ permalink raw reply related [flat|nested] 42+ messages in thread* [RFC PATCH 7/8] memcg: Enable logging of memory control mitigation action
[not found] ` <20200817140831.30260-1-longman-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2020-08-17 14:08 ` [RFC PATCH 3/8] memcg: Allow the use of task RSS memory as over-high action trigger Waiman Long
@ 2020-08-17 14:08 ` Waiman Long
2020-08-18 9:14 ` [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control peterz-wEGCiKHe2LqWVfeAwA7xHQ
2 siblings, 0 replies; 42+ messages in thread
From: Waiman Long @ 2020-08-17 14:08 UTC (permalink / raw)
To: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Peter Zijlstra,
Juri Lelli, Vincent Guittot
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
cgroups-u79uwXL29TY76Z2rM5mHXA, linux-mm-Bw31MaZKKs3YtjvyW6yDsg,
Waiman Long
Some of the migitation actions of PR_SET_MEMCONTROL give no visible
signal that some actions are being done inside the kernel. To make it
more visble, a new PR_MEMFLAG_LOG flag is added to enable the logging
of the migitation action done in the kernel ring buffer.
The logging is done once when the mitigation action starts through the
setting of an internal PR_MEMFLAG_LOGGED flag. This flag will be cleared
when it is detected that the memory limit no longer exceeds memory.high.
Signed-off-by: Waiman Long <longman-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
include/uapi/linux/prctl.h | 1 +
mm/memcontrol.c | 34 +++++++++++++++++++++++++++++++++-
2 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index 7ba40e10737d..faa7a51fc52a 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -266,6 +266,7 @@ struct prctl_mm_map {
/* Flags for PR_SET_MEMCONTROL */
# define PR_MEMFLAG_SIGCONT (1UL << 0) /* Continuous signal delivery */
# define PR_MEMFLAG_DIRECT (1UL << 1) /* Direct memory limit */
+# define PR_MEMFLAG_LOG (1UL << 2) /* Log action done */
# define PR_MEMFLAG_RSS_ANON (1UL << 8) /* Check anonymous pages */
# define PR_MEMFLAG_RSS_FILE (1UL << 9) /* Check file pages */
# define PR_MEMFLAG_RSS_SHMEM (1UL << 10) /* Check shmem pages */
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index bddf3e659469..5bda2dd755fc 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2640,6 +2640,7 @@ get_rss_counter(struct mm_struct *mm, int mm_bit, u16 flags, int rss_bit)
* Return true if an action has been taken or further check is not needed,
* false otherwise.
*/
+#define PR_MEMFLAG_LOGGED (1UL << 7) /* A log message printed */
static bool __mem_cgroup_over_high_action(struct mem_cgroup *memcg, u8 action,
u16 flags)
{
@@ -2714,6 +2715,32 @@ static bool __mem_cgroup_over_high_action(struct mem_cgroup *memcg, u8 action,
break;
}
+ if ((flags & (PR_MEMFLAG_LOG|PR_MEMFLAG_LOGGED)) == PR_MEMFLAG_LOG) {
+ char name[80];
+ static const char * const acts[] = {
+ [PR_MEMACT_ENOMEM] = "Action: return ENOMEM on some syscalls",
+ [PR_MEMACT_SLOWDOWN] = "Action: slow down process",
+ [PR_MEMACT_SIGNAL] = "Action: send signal",
+ [PR_MEMACT_KILL] = "Action: kill the process",
+ };
+
+ name[0] = '\0';
+ if (memcg)
+ cgroup_name(memcg->css.cgroup, name, sizeof(name));
+ else
+ strcpy(name, "N/A");
+
+ /*
+ * Use printk_deferred() to minimize delay in the memory
+ * allocation path.
+ */
+ printk_deferred(KERN_INFO
+ "Cgroup: %s, Comm: %s, Pid: %d, Mem: %ld pages, %s\n",
+ name, current->comm, current->pid, mem, acts[action]);
+ WRITE_ONCE(current->memcg_over_high_flags,
+ flags | PR_MEMFLAG_LOGGED);
+ }
+
out:
mmput(mm);
/*
@@ -2740,8 +2767,13 @@ static inline bool mem_cgroup_over_high_action(struct mem_cgroup *memcg,
if (flags & PR_MEMFLAG_DIRECT)
memcg = NULL; /* Direct per-task memory limit checking */
- else if (!mem_high)
+ else if (!mem_high) {
+ /* Clear the PR_MEMFLAG_LOGGED flag, if set */
+ if (flags & PR_MEMFLAG_LOGGED)
+ WRITE_ONCE(current->memcg_over_high_flags,
+ flags & ~PR_MEMFLAG_LOGGED);
return false;
+ }
return __mem_cgroup_over_high_action(memcg, action, flags);
}
--
2.18.1
^ permalink raw reply related [flat|nested] 42+ messages in thread* Re: [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control
[not found] ` <20200817140831.30260-1-longman-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2020-08-17 14:08 ` [RFC PATCH 3/8] memcg: Allow the use of task RSS memory as over-high action trigger Waiman Long
2020-08-17 14:08 ` [RFC PATCH 7/8] memcg: Enable logging of memory control mitigation action Waiman Long
@ 2020-08-18 9:14 ` peterz-wEGCiKHe2LqWVfeAwA7xHQ
[not found] ` <20200818091453.GL2674-Nxj+rRp3nVydTX5a5knrm8zTDFooKrT+cvkQGrU6aU0@public.gmane.org>
2020-08-18 19:27 ` Waiman Long
2 siblings, 2 replies; 42+ messages in thread
From: peterz-wEGCiKHe2LqWVfeAwA7xHQ @ 2020-08-18 9:14 UTC (permalink / raw)
To: Waiman Long
Cc: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Juri Lelli,
Vincent Guittot, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
cgroups-u79uwXL29TY76Z2rM5mHXA, linux-mm-Bw31MaZKKs3YtjvyW6yDsg
On Mon, Aug 17, 2020 at 10:08:23AM -0400, Waiman Long wrote:
> Memory controller can be used to control and limit the amount of
> physical memory used by a task. When a limit is set in "memory.high" in
> a v2 non-root memory cgroup, the memory controller will try to reclaim
> memory if the limit has been exceeded. Normally, that will be enough
> to keep the physical memory consumption of tasks in the memory cgroup
> to be around or below the "memory.high" limit.
>
> Sometimes, memory reclaim may not be able to recover memory in a rate
> that can catch up to the physical memory allocation rate. In this case,
> the physical memory consumption will keep on increasing.
Then slow down the allocator? That's what we do for dirty pages too, we
slow down the dirtier when we run against the limits.
^ permalink raw reply [flat|nested] 42+ messages in thread[parent not found: <20200818091453.GL2674-Nxj+rRp3nVydTX5a5knrm8zTDFooKrT+cvkQGrU6aU0@public.gmane.org>]
* Re: [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control
[not found] ` <20200818091453.GL2674-Nxj+rRp3nVydTX5a5knrm8zTDFooKrT+cvkQGrU6aU0@public.gmane.org>
@ 2020-08-18 9:26 ` Michal Hocko
[not found] ` <20200818092617.GN28270-2MMpYkNvuYDjFM9bn6wA6Q@public.gmane.org>
2020-08-18 9:27 ` Chris Down
1 sibling, 1 reply; 42+ messages in thread
From: Michal Hocko @ 2020-08-18 9:26 UTC (permalink / raw)
To: peterz-wEGCiKHe2LqWVfeAwA7xHQ
Cc: Waiman Long, Andrew Morton, Johannes Weiner, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Juri Lelli,
Vincent Guittot, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
cgroups-u79uwXL29TY76Z2rM5mHXA, linux-mm-Bw31MaZKKs3YtjvyW6yDsg
On Tue 18-08-20 11:14:53, Peter Zijlstra wrote:
> On Mon, Aug 17, 2020 at 10:08:23AM -0400, Waiman Long wrote:
> > Memory controller can be used to control and limit the amount of
> > physical memory used by a task. When a limit is set in "memory.high" in
> > a v2 non-root memory cgroup, the memory controller will try to reclaim
> > memory if the limit has been exceeded. Normally, that will be enough
> > to keep the physical memory consumption of tasks in the memory cgroup
> > to be around or below the "memory.high" limit.
> >
> > Sometimes, memory reclaim may not be able to recover memory in a rate
> > that can catch up to the physical memory allocation rate. In this case,
> > the physical memory consumption will keep on increasing.
>
> Then slow down the allocator? That's what we do for dirty pages too, we
> slow down the dirtier when we run against the limits.
This is what we actually do. Have a look at mem_cgroup_handle_over_high.
--
Michal Hocko
SUSE Labs
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control
[not found] ` <20200818091453.GL2674-Nxj+rRp3nVydTX5a5knrm8zTDFooKrT+cvkQGrU6aU0@public.gmane.org>
2020-08-18 9:26 ` Michal Hocko
@ 2020-08-18 9:27 ` Chris Down
2020-08-18 10:04 ` peterz
2020-08-18 19:30 ` Waiman Long
1 sibling, 2 replies; 42+ messages in thread
From: Chris Down @ 2020-08-18 9:27 UTC (permalink / raw)
To: peterz-wEGCiKHe2LqWVfeAwA7xHQ
Cc: Waiman Long, Andrew Morton, Johannes Weiner, Michal Hocko,
Vladimir Davydov, Jonathan Corbet, Alexey Dobriyan, Ingo Molnar,
Juri Lelli, Vincent Guittot, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
cgroups-u79uwXL29TY76Z2rM5mHXA, linux-mm-Bw31MaZKKs3YtjvyW6yDsg
peterz-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org writes:
>On Mon, Aug 17, 2020 at 10:08:23AM -0400, Waiman Long wrote:
>> Memory controller can be used to control and limit the amount of
>> physical memory used by a task. When a limit is set in "memory.high" in
>> a v2 non-root memory cgroup, the memory controller will try to reclaim
>> memory if the limit has been exceeded. Normally, that will be enough
>> to keep the physical memory consumption of tasks in the memory cgroup
>> to be around or below the "memory.high" limit.
>>
>> Sometimes, memory reclaim may not be able to recover memory in a rate
>> that can catch up to the physical memory allocation rate. In this case,
>> the physical memory consumption will keep on increasing.
>
>Then slow down the allocator? That's what we do for dirty pages too, we
>slow down the dirtier when we run against the limits.
We already do that since v5.4. I'm wondering whether Waiman's customer is just
running with a too-old kernel without 0e4b01df865 ("mm, memcg: throttle
allocators when failing reclaim over memory.high") backported.
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control
2020-08-18 9:27 ` Chris Down
@ 2020-08-18 10:04 ` peterz
[not found] ` <20200818100444.GN2674-Nxj+rRp3nVydTX5a5knrm8zTDFooKrT+cvkQGrU6aU0@public.gmane.org>
2020-08-18 19:30 ` Waiman Long
1 sibling, 1 reply; 42+ messages in thread
From: peterz @ 2020-08-18 10:04 UTC (permalink / raw)
To: Chris Down
Cc: Waiman Long, Andrew Morton, Johannes Weiner, Michal Hocko,
Vladimir Davydov, Jonathan Corbet, Alexey Dobriyan, Ingo Molnar,
Juri Lelli, Vincent Guittot, linux-kernel, linux-doc,
linux-fsdevel, cgroups, linux-mm
On Tue, Aug 18, 2020 at 10:27:37AM +0100, Chris Down wrote:
> peterz@infradead.org writes:
> > On Mon, Aug 17, 2020 at 10:08:23AM -0400, Waiman Long wrote:
> > > Memory controller can be used to control and limit the amount of
> > > physical memory used by a task. When a limit is set in "memory.high" in
> > > a v2 non-root memory cgroup, the memory controller will try to reclaim
> > > memory if the limit has been exceeded. Normally, that will be enough
> > > to keep the physical memory consumption of tasks in the memory cgroup
> > > to be around or below the "memory.high" limit.
> > >
> > > Sometimes, memory reclaim may not be able to recover memory in a rate
> > > that can catch up to the physical memory allocation rate. In this case,
> > > the physical memory consumption will keep on increasing.
> >
> > Then slow down the allocator? That's what we do for dirty pages too, we
> > slow down the dirtier when we run against the limits.
>
> We already do that since v5.4. I'm wondering whether Waiman's customer is
> just running with a too-old kernel without 0e4b01df865 ("mm, memcg: throttle
> allocators when failing reclaim over memory.high") backported.
That commit is fundamentally broken, it doesn't guarantee anything.
Please go read how the dirty throttling works (unless people wrecked
that since..).
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control
2020-08-18 9:27 ` Chris Down
2020-08-18 10:04 ` peterz
@ 2020-08-18 19:30 ` Waiman Long
1 sibling, 0 replies; 42+ messages in thread
From: Waiman Long @ 2020-08-18 19:30 UTC (permalink / raw)
To: Chris Down, peterz
Cc: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Juri Lelli,
Vincent Guittot, linux-kernel, linux-doc, linux-fsdevel, cgroups,
linux-mm
On 8/18/20 5:27 AM, Chris Down wrote:
> peterz@infradead.org writes:
>> On Mon, Aug 17, 2020 at 10:08:23AM -0400, Waiman Long wrote:
>>> Memory controller can be used to control and limit the amount of
>>> physical memory used by a task. When a limit is set in "memory.high" in
>>> a v2 non-root memory cgroup, the memory controller will try to reclaim
>>> memory if the limit has been exceeded. Normally, that will be enough
>>> to keep the physical memory consumption of tasks in the memory cgroup
>>> to be around or below the "memory.high" limit.
>>>
>>> Sometimes, memory reclaim may not be able to recover memory in a rate
>>> that can catch up to the physical memory allocation rate. In this case,
>>> the physical memory consumption will keep on increasing.
>>
>> Then slow down the allocator? That's what we do for dirty pages too, we
>> slow down the dirtier when we run against the limits.
>
> We already do that since v5.4. I'm wondering whether Waiman's customer
> is just running with a too-old kernel without 0e4b01df865 ("mm, memcg:
> throttle allocators when failing reclaim over memory.high") backported.
>
The fact is that we don't have that in RHEL8 yet and cgroup v2 is still
not the default at the moment.
I am planning to backport the throttling patches to RHEL and hopefully
can switch to use cgroup v2 soon.
Cheers,
Longman
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control
2020-08-18 9:14 ` [RFC PATCH 0/8] memcg: Enable fine-grained per process memory control peterz-wEGCiKHe2LqWVfeAwA7xHQ
[not found] ` <20200818091453.GL2674-Nxj+rRp3nVydTX5a5knrm8zTDFooKrT+cvkQGrU6aU0@public.gmane.org>
@ 2020-08-18 19:27 ` Waiman Long
1 sibling, 0 replies; 42+ messages in thread
From: Waiman Long @ 2020-08-18 19:27 UTC (permalink / raw)
To: peterz
Cc: Andrew Morton, Johannes Weiner, Michal Hocko, Vladimir Davydov,
Jonathan Corbet, Alexey Dobriyan, Ingo Molnar, Juri Lelli,
Vincent Guittot, linux-kernel, linux-doc, linux-fsdevel, cgroups,
linux-mm
On 8/18/20 5:14 AM, peterz@infradead.org wrote:
> On Mon, Aug 17, 2020 at 10:08:23AM -0400, Waiman Long wrote:
>> Memory controller can be used to control and limit the amount of
>> physical memory used by a task. When a limit is set in "memory.high" in
>> a v2 non-root memory cgroup, the memory controller will try to reclaim
>> memory if the limit has been exceeded. Normally, that will be enough
>> to keep the physical memory consumption of tasks in the memory cgroup
>> to be around or below the "memory.high" limit.
>>
>> Sometimes, memory reclaim may not be able to recover memory in a rate
>> that can catch up to the physical memory allocation rate. In this case,
>> the physical memory consumption will keep on increasing.
> Then slow down the allocator? That's what we do for dirty pages too, we
> slow down the dirtier when we run against the limits.
>
I missed that there are already allocator throttling done in upstream
code. So I will need to reexamine if this patch is necessary or not.
Thanks,
Longman
^ permalink raw reply [flat|nested] 42+ messages in thread