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 28static size_t page_size; 29static struct tst_cg_group *cg_child; 30static int fd; 31static int file_to_all_error = 10; 32 33static 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", ¤t); 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 59static 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", ¤t); 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", ¤t); 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 87static 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", ¤t); 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", ¤t); 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 112static 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 125static void cleanup(void) 126{ 127 if (cg_child) 128 cg_child = tst_cg_group_rm(cg_child); 129} 130 131static 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