• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*\
3  *
4  * [Description]
5  *
6  * Conversion of second kself test in cgroup/test_memcontrol.c.
7  *
8  * Original description:
9  * "This test creates a memory cgroup, allocates some anonymous memory
10  * and some pagecache and check memory.current and some memory.stat
11  * values."
12  *
13  * Note that the V1 rss and cache counters were renamed to anon and
14  * file in V2. Besides error reporting, this test differs from the
15  * kselftest in the following ways:
16  *
17  * . It supports V1.
18  * . It writes instead of reads to fill the page cache. Because no
19  *   pages were allocated on tmpfs.
20  * . It runs on most filesystems available
21  * . On EXFAT and extN we change the margin of error between all and file
22  *   memory to 50%. Because these allocate non-page-cache memory during writes.
23  */
24 #define _GNU_SOURCE
25 
26 #include "memcontrol_common.h"
27 
28 static size_t page_size;
29 static struct tst_cg_group *cg_child;
30 static int fd;
31 static int file_to_all_error = 10;
32 
alloc_anon_50M_check(void)33 static void alloc_anon_50M_check(void)
34 {
35 	const ssize_t size = MB(50);
36 	char *buf, *ptr;
37 	ssize_t anon, current;
38 	const char *const anon_key_fmt =
39 		TST_CG_VER_IS_V1(tst_cg, "memory") ? "rss %zd" : "anon %zd";
40 
41 	buf = SAFE_MALLOC(size);
42 	for (ptr = buf; ptr < buf + size; ptr += page_size)
43 		*ptr = 0;
44 
45 	SAFE_CG_SCANF(cg_child, "memory.current", "%zd", &current);
46 	TST_EXP_EXPR(current >= size,
47 		     "(memory.current=%zd) >= (size=%zd)", current, size);
48 
49 	SAFE_CG_LINES_SCANF(cg_child, "memory.stat", anon_key_fmt, &anon);
50 
51 	TST_EXP_EXPR(anon > 0, "(memory.stat.anon=%zd) > 0", anon);
52 	TST_EXP_EXPR(values_close(size, anon, 3),
53 		     "(size=%zd) ~= (memory.stat.anon=%zd)", size, anon);
54 	TST_EXP_EXPR(values_close(anon, current, 3),
55 		     "(memory.current=%zd) ~= (memory.stat.anon=%zd)",
56 		     current, anon);
57 }
58 
alloc_pagecache_50M_check(void)59 static void alloc_pagecache_50M_check(void)
60 {
61 	const size_t size = MB(50);
62 	size_t current, file;
63 	const char *const file_key_fmt =
64 		TST_CG_VER_IS_V1(tst_cg, "memory") ? "cache %zd" : "file %zd";
65 
66 	fd = SAFE_OPEN(TMPDIR"/tmpfile", O_RDWR | O_CREAT, 0600);
67 
68 	SAFE_CG_SCANF(cg_child, "memory.current", "%zu", &current);
69 	tst_res(TINFO, "Created temp file: memory.current=%zu", current);
70 
71 	alloc_pagecache(fd, size);
72 
73 	SAFE_CG_SCANF(cg_child, "memory.current", "%zu", &current);
74 	TST_EXP_EXPR(current >= size,
75 			 "(memory.current=%zu) >= (size=%zu)", current, size);
76 
77 	SAFE_CG_LINES_SCANF(cg_child, "memory.stat", file_key_fmt, &file);
78 	TST_EXP_EXPR(file > 0, "(memory.stat.file=%zd) > 0", file);
79 
80 	TST_EXP_EXPR(values_close(file, current, file_to_all_error),
81 			 "(memory.current=%zd) ~= (memory.stat.file=%zd)",
82 			 current, file);
83 
84 	SAFE_CLOSE(fd);
85 }
86 
test_memcg_current(unsigned int n)87 static void test_memcg_current(unsigned int n)
88 {
89 	size_t current;
90 
91 	cg_child = tst_cg_group_mk(tst_cg, "child");
92 	SAFE_CG_SCANF(cg_child, "memory.current", "%zu", &current);
93 	TST_EXP_EXPR(current == 0, "(current=%zu) == 0", current);
94 
95 	if (!SAFE_FORK()) {
96 		SAFE_CG_PRINTF(cg_child, "cgroup.procs", "%d", getpid());
97 
98 		SAFE_CG_SCANF(cg_child, "memory.current", "%zu", &current);
99 		tst_res(TINFO, "Added proc to memcg: memory.current=%zu",
100 			current);
101 
102 		if (!n)
103 			alloc_anon_50M_check();
104 		else
105 			alloc_pagecache_50M_check();
106 	} else {
107 		tst_reap_children();
108 		cg_child = tst_cg_group_rm(cg_child);
109 	}
110 }
111 
setup(void)112 static void setup(void)
113 {
114 	page_size = SAFE_SYSCONF(_SC_PAGESIZE);
115 
116 	switch (tst_fs_type(TMPDIR)) {
117 	case TST_VFAT_MAGIC:
118 	case TST_EXFAT_MAGIC:
119 	case TST_EXT234_MAGIC:
120 		file_to_all_error = 50;
121 		break;
122 	}
123 }
124 
cleanup(void)125 static void cleanup(void)
126 {
127 	if (cg_child)
128 		cg_child = tst_cg_group_rm(cg_child);
129 }
130 
131 static struct tst_test test = {
132 	.setup = setup,
133 	.cleanup = cleanup,
134 	.tcnt = 2,
135 	.test = test_memcg_current,
136 	.mount_device = 1,
137 	.dev_min_size = 256,
138 	.mntpoint = TMPDIR,
139 	.all_filesystems = 1,
140 	.forks_child = 1,
141 	.needs_root = 1,
142 	.needs_cgroup_ctrls = (const char *const []){ "memory", NULL },
143 };
144