Commit eae3cb2e authored by Yosry Ahmed's avatar Yosry Ahmed Committed by Andrew Morton
Browse files

selftests: cgroup: add a selftest for memory.reclaim

Add a new test for memory.reclaim that verifies that the interface
correctly reclaims memory as intended, from both anon and file pages.

Link: https://lkml.kernel.org/r/20220425190040.2475377-5-yosryahmed@google.com


Signed-off-by: default avatarYosry Ahmed <yosryahmed@google.com>
Acked-by: default avatarRoman Gushchin <roman.gushchin@linux.dev>
Acked-by: default avatarDavid Rientjes <rientjes@google.com>
Cc: Chen Wandun <chenwandun@huawei.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Michal Hocko <mhocko@suse.com>
Cc: "Michal Koutn" <mkoutny@suse.com>
Cc: Shakeel Butt <shakeelb@google.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Tim Chen <tim.c.chen@linux.intel.com>
Cc: Vaibhav Jain <vaibhav@linux.ibm.com>
Cc: Wei Xu <weixugc@google.com>
Cc: Yu Zhao <yuzhao@google.com>
Cc: Zefan Li <lizefan.x@bytedance.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent a3622a53
Loading
Loading
Loading
Loading
+106 −0
Original line number Diff line number Diff line
@@ -760,6 +760,111 @@ static int test_memcg_max(const char *root)
	return ret;
}

/*
 * This test checks that memory.reclaim reclaims the given
 * amount of memory (from both anon and file, if possible).
 */
static int test_memcg_reclaim(const char *root)
{
	int ret = KSFT_FAIL, fd, retries;
	char *memcg;
	long current, expected_usage, to_reclaim;
	char buf[64];

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

	if (cg_create(memcg))
		goto cleanup;

	current = cg_read_long(memcg, "memory.current");
	if (current != 0)
		goto cleanup;

	fd = get_temp_fd();
	if (fd < 0)
		goto cleanup;

	cg_run_nowait(memcg, alloc_pagecache_50M_noexit, (void *)(long)fd);

	/*
	 * If swap is enabled, try to reclaim from both anon and file, else try
	 * to reclaim from file only.
	 */
	if (is_swap_enabled()) {
		cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(50));
		expected_usage = MB(100);
	} else
		expected_usage = MB(50);

	/*
	 * Wait until current usage reaches the expected usage (or we run out of
	 * retries).
	 */
	retries = 5;
	while (!values_close(cg_read_long(memcg, "memory.current"),
			    expected_usage, 10)) {
		if (retries--) {
			sleep(1);
			continue;
		} else {
			fprintf(stderr,
				"failed to allocate %ld for memcg reclaim test\n",
				expected_usage);
			goto cleanup;
		}
	}

	/*
	 * Reclaim until current reaches 30M, this makes sure we hit both anon
	 * and file if swap is enabled.
	 */
	retries = 5;
	while (true) {
		int err;

		current = cg_read_long(memcg, "memory.current");
		to_reclaim = current - MB(30);

		/*
		 * We only keep looping if we get EAGAIN, which means we could
		 * not reclaim the full amount.
		 */
		if (to_reclaim <= 0)
			goto cleanup;


		snprintf(buf, sizeof(buf), "%ld", to_reclaim);
		err = cg_write(memcg, "memory.reclaim", buf);
		if (!err) {
			/*
			 * If writing succeeds, then the written amount should have been
			 * fully reclaimed (and maybe more).
			 */
			current = cg_read_long(memcg, "memory.current");
			if (!values_close(current, MB(30), 3) && current > MB(30))
				goto cleanup;
			break;
		}

		/* The kernel could not reclaim the full amount, try again. */
		if (err == -EAGAIN && retries--)
			continue;

		/* We got an unexpected error or ran out of retries. */
		goto cleanup;
	}

	ret = KSFT_PASS;
cleanup:
	cg_destroy(memcg);
	free(memcg);
	close(fd);

	return ret;
}

static int alloc_anon_50M_check_swap(const char *cgroup, void *arg)
{
	long mem_max = (long)arg;
@@ -1264,6 +1369,7 @@ struct memcg_test {
	T(test_memcg_high),
	T(test_memcg_high_sync),
	T(test_memcg_max),
	T(test_memcg_reclaim),
	T(test_memcg_oom_events),
	T(test_memcg_swap_max),
	T(test_memcg_sock),