Skip to content

Commit

Permalink
selftests: memcg: test high limit for single entry allocation
Browse files Browse the repository at this point in the history
Test the enforcement of memory.high limit for large amount of memory
allocation within a single kernel entry.  There are valid use-cases
where the application can trigger large amount of memory allocation
within a single syscall e.g.  mlock() or mmap(MAP_POPULATE).

Make sure memory.high limit enforcement works for such use-cases.

Link: https://lkml.kernel.org/r/[email protected]
Signed-off-by: Shakeel Butt <[email protected]>
Reviewed-by: Roman Gushchin <[email protected]>
Cc: Roman Gushchin <[email protected]>
Cc: Chris Down <[email protected]>
Cc: Johannes Weiner <[email protected]>
Cc: Michal Hocko <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
shakeelb authored and torvalds committed Mar 22, 2022
1 parent 1461e8c commit 6323ec5
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 3 deletions.
15 changes: 12 additions & 3 deletions tools/testing/selftests/cgroup/cgroup_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -583,16 +583,15 @@ int clone_into_cgroup_run_wait(const char *cgroup)
return 0;
}

int cg_prepare_for_wait(const char *cgroup)
static int __prepare_for_wait(const char *cgroup, const char *filename)
{
int fd, ret = -1;

fd = inotify_init1(0);
if (fd == -1)
return fd;

ret = inotify_add_watch(fd, cg_control(cgroup, "cgroup.events"),
IN_MODIFY);
ret = inotify_add_watch(fd, cg_control(cgroup, filename), IN_MODIFY);
if (ret == -1) {
close(fd);
fd = -1;
Expand All @@ -601,6 +600,16 @@ int cg_prepare_for_wait(const char *cgroup)
return fd;
}

int cg_prepare_for_wait(const char *cgroup)
{
return __prepare_for_wait(cgroup, "cgroup.events");
}

int memcg_prepare_for_wait(const char *cgroup)
{
return __prepare_for_wait(cgroup, "memory.events");
}

int cg_wait_for(int fd)
{
int ret = -1;
Expand Down
1 change: 1 addition & 0 deletions tools/testing/selftests/cgroup/cgroup_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,5 @@ extern int clone_reap(pid_t pid, int options);
extern int clone_into_cgroup_run_wait(const char *cgroup);
extern int dirfd_open_opath(const char *dir);
extern int cg_prepare_for_wait(const char *cgroup);
extern int memcg_prepare_for_wait(const char *cgroup);
extern int cg_wait_for(int fd);
78 changes: 78 additions & 0 deletions tools/testing/selftests/cgroup/test_memcontrol.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <sys/mman.h>

#include "../kselftest.h"
#include "cgroup_util.h"
Expand Down Expand Up @@ -628,6 +629,82 @@ static int test_memcg_high(const char *root)
return ret;
}

static int alloc_anon_mlock(const char *cgroup, void *arg)
{
size_t size = (size_t)arg;
void *buf;

buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
0, 0);
if (buf == MAP_FAILED)
return -1;

mlock(buf, size);
munmap(buf, size);
return 0;
}

/*
* This test checks that memory.high is able to throttle big single shot
* allocation i.e. large allocation within one kernel entry.
*/
static int test_memcg_high_sync(const char *root)
{
int ret = KSFT_FAIL, pid, fd = -1;
char *memcg;
long pre_high, pre_max;
long post_high, post_max;

memcg = cg_name(root, "memcg_test");
if (!memcg)
goto cleanup;

if (cg_create(memcg))
goto cleanup;

pre_high = cg_read_key_long(memcg, "memory.events", "high ");
pre_max = cg_read_key_long(memcg, "memory.events", "max ");
if (pre_high < 0 || pre_max < 0)
goto cleanup;

if (cg_write(memcg, "memory.swap.max", "0"))
goto cleanup;

if (cg_write(memcg, "memory.high", "30M"))
goto cleanup;

if (cg_write(memcg, "memory.max", "140M"))
goto cleanup;

fd = memcg_prepare_for_wait(memcg);
if (fd < 0)
goto cleanup;

pid = cg_run_nowait(memcg, alloc_anon_mlock, (void *)MB(200));
if (pid < 0)
goto cleanup;

cg_wait_for(fd);

post_high = cg_read_key_long(memcg, "memory.events", "high ");
post_max = cg_read_key_long(memcg, "memory.events", "max ");
if (post_high < 0 || post_max < 0)
goto cleanup;

if (pre_high == post_high || pre_max != post_max)
goto cleanup;

ret = KSFT_PASS;

cleanup:
if (fd >= 0)
close(fd);
cg_destroy(memcg);
free(memcg);

return ret;
}

/*
* This test checks that memory.max limits the amount of
* memory which can be consumed by either anonymous memory
Expand Down Expand Up @@ -1180,6 +1257,7 @@ struct memcg_test {
T(test_memcg_min),
T(test_memcg_low),
T(test_memcg_high),
T(test_memcg_high_sync),
T(test_memcg_max),
T(test_memcg_oom_events),
T(test_memcg_swap_max),
Expand Down

0 comments on commit 6323ec5

Please sign in to comment.