1f08c3bdfSopenharmony_ci/* 2f08c3bdfSopenharmony_ci * Copyright (C) 2012-2017 Red Hat, Inc. 3f08c3bdfSopenharmony_ci * 4f08c3bdfSopenharmony_ci * This program is free software; you can redistribute it and/or modify 5f08c3bdfSopenharmony_ci * it under the terms of the GNU General Public License as published by 6f08c3bdfSopenharmony_ci * the Free Software Foundation; either version 2 of the License, or 7f08c3bdfSopenharmony_ci * (at your option) any later version. 8f08c3bdfSopenharmony_ci * 9f08c3bdfSopenharmony_ci * This program is distributed in the hope that it will be useful, 10f08c3bdfSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 11f08c3bdfSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 12f08c3bdfSopenharmony_ci * the GNU General Public License for more details. 13f08c3bdfSopenharmony_ci * 14f08c3bdfSopenharmony_ci * Description: 15f08c3bdfSopenharmony_ci * 16f08c3bdfSopenharmony_ci * The program is designed to test max_map_count tunable file 17f08c3bdfSopenharmony_ci * 18f08c3bdfSopenharmony_ci * The kernel Documentation say that: 19f08c3bdfSopenharmony_ci * /proc/sys/vm/max_map_count contains the maximum number of memory map 20f08c3bdfSopenharmony_ci * areas a process may have. Memory map areas are used as a side-effect 21f08c3bdfSopenharmony_ci * of calling malloc, directly by mmap and mprotect, and also when 22f08c3bdfSopenharmony_ci * loading shared libraries. 23f08c3bdfSopenharmony_ci * 24f08c3bdfSopenharmony_ci * Each process has his own maps file: /proc/[pid]/maps, and each line 25f08c3bdfSopenharmony_ci * indicates a map entry, so it can caculate the amount of maps by reading 26f08c3bdfSopenharmony_ci * the file lines' number to check the tunable performance. 27f08c3bdfSopenharmony_ci * 28f08c3bdfSopenharmony_ci * The program tries to invoke mmap() endlessly until it triggers MAP_FAILED, 29f08c3bdfSopenharmony_ci * then reads the process's maps file /proc/[pid]/maps, save the line number to 30f08c3bdfSopenharmony_ci * map_count variable, and compare it with /proc/sys/vm/max_map_count, 31f08c3bdfSopenharmony_ci * map_count should be greater than max_map_count by 1; 32f08c3bdfSopenharmony_ci * 33f08c3bdfSopenharmony_ci * Note: On some architectures there is a special vma VSYSCALL, which 34f08c3bdfSopenharmony_ci * is allocated without incrementing mm->map_count variable. On these 35f08c3bdfSopenharmony_ci * architectures each /proc/<pid>/maps has at the end: 36f08c3bdfSopenharmony_ci * ... 37f08c3bdfSopenharmony_ci * ... 38f08c3bdfSopenharmony_ci * ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] 39f08c3bdfSopenharmony_ci * 40f08c3bdfSopenharmony_ci * so we ignore this line during /proc/[pid]/maps reading. 41f08c3bdfSopenharmony_ci */ 42f08c3bdfSopenharmony_ci 43f08c3bdfSopenharmony_ci#define _GNU_SOURCE 44f08c3bdfSopenharmony_ci#include <sys/wait.h> 45f08c3bdfSopenharmony_ci#include <errno.h> 46f08c3bdfSopenharmony_ci#include <fcntl.h> 47f08c3bdfSopenharmony_ci#include <stdbool.h> 48f08c3bdfSopenharmony_ci#include <stdio.h> 49f08c3bdfSopenharmony_ci#include <stdlib.h> 50f08c3bdfSopenharmony_ci#include <sys/utsname.h> 51f08c3bdfSopenharmony_ci#include "mem.h" 52f08c3bdfSopenharmony_ci 53f08c3bdfSopenharmony_ci#define MAP_COUNT_DEFAULT 1024 54f08c3bdfSopenharmony_ci#define MAX_MAP_COUNT 65536L 55f08c3bdfSopenharmony_ci 56f08c3bdfSopenharmony_ci/* This is a filter to exclude map entries which aren't accounted 57f08c3bdfSopenharmony_ci * for in the vm_area_struct's map_count. 58f08c3bdfSopenharmony_ci */ 59f08c3bdfSopenharmony_cistatic bool filter_map(const char *line) 60f08c3bdfSopenharmony_ci{ 61f08c3bdfSopenharmony_ci char buf[BUFSIZ]; 62f08c3bdfSopenharmony_ci int ret; 63f08c3bdfSopenharmony_ci 64f08c3bdfSopenharmony_ci ret = sscanf(line, "%*p-%*p %*4s %*p %*2d:%*2d %*d %s", buf); 65f08c3bdfSopenharmony_ci if (ret != 1) 66f08c3bdfSopenharmony_ci return false; 67f08c3bdfSopenharmony_ci 68f08c3bdfSopenharmony_ci switch (tst_arch.type) { 69f08c3bdfSopenharmony_ci case TST_X86: 70f08c3bdfSopenharmony_ci case TST_X86_64: 71f08c3bdfSopenharmony_ci /* On x86, there's an old compat vsyscall page */ 72f08c3bdfSopenharmony_ci if (!strcmp(buf, "[vsyscall]")) 73f08c3bdfSopenharmony_ci return true; 74f08c3bdfSopenharmony_ci break; 75f08c3bdfSopenharmony_ci case TST_IA64: 76f08c3bdfSopenharmony_ci /* On ia64, the vdso is not a proper mapping */ 77f08c3bdfSopenharmony_ci if (!strcmp(buf, "[vdso]")) 78f08c3bdfSopenharmony_ci return true; 79f08c3bdfSopenharmony_ci break; 80f08c3bdfSopenharmony_ci case TST_ARM: 81f08c3bdfSopenharmony_ci /* Skip it when run it in aarch64 */ 82f08c3bdfSopenharmony_ci if (tst_kernel_bits() == 64) 83f08c3bdfSopenharmony_ci return false; 84f08c3bdfSopenharmony_ci 85f08c3bdfSopenharmony_ci /* Older arm kernels didn't label their vdso maps */ 86f08c3bdfSopenharmony_ci if (!strncmp(line, "ffff0000-ffff1000", 17)) 87f08c3bdfSopenharmony_ci return true; 88f08c3bdfSopenharmony_ci break; 89f08c3bdfSopenharmony_ci default: 90f08c3bdfSopenharmony_ci break; 91f08c3bdfSopenharmony_ci }; 92f08c3bdfSopenharmony_ci 93f08c3bdfSopenharmony_ci return false; 94f08c3bdfSopenharmony_ci} 95f08c3bdfSopenharmony_ci 96f08c3bdfSopenharmony_cistatic long count_maps(pid_t pid) 97f08c3bdfSopenharmony_ci{ 98f08c3bdfSopenharmony_ci FILE *fp; 99f08c3bdfSopenharmony_ci size_t len; 100f08c3bdfSopenharmony_ci char *line = NULL; 101f08c3bdfSopenharmony_ci char buf[BUFSIZ]; 102f08c3bdfSopenharmony_ci long map_count = 0; 103f08c3bdfSopenharmony_ci 104f08c3bdfSopenharmony_ci snprintf(buf, BUFSIZ, "/proc/%d/maps", pid); 105f08c3bdfSopenharmony_ci fp = fopen(buf, "r"); 106f08c3bdfSopenharmony_ci if (fp == NULL) 107f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "fopen %s", buf); 108f08c3bdfSopenharmony_ci while (getline(&line, &len, fp) != -1) { 109f08c3bdfSopenharmony_ci /* exclude vdso and vsyscall */ 110f08c3bdfSopenharmony_ci if (filter_map(line)) 111f08c3bdfSopenharmony_ci continue; 112f08c3bdfSopenharmony_ci map_count++; 113f08c3bdfSopenharmony_ci } 114f08c3bdfSopenharmony_ci fclose(fp); 115f08c3bdfSopenharmony_ci 116f08c3bdfSopenharmony_ci return map_count; 117f08c3bdfSopenharmony_ci} 118f08c3bdfSopenharmony_ci 119f08c3bdfSopenharmony_cistatic void max_map_count_test(void) 120f08c3bdfSopenharmony_ci{ 121f08c3bdfSopenharmony_ci int status; 122f08c3bdfSopenharmony_ci pid_t pid; 123f08c3bdfSopenharmony_ci long max_maps; 124f08c3bdfSopenharmony_ci long map_count; 125f08c3bdfSopenharmony_ci long max_iters; 126f08c3bdfSopenharmony_ci long memfree; 127f08c3bdfSopenharmony_ci 128f08c3bdfSopenharmony_ci /* 129f08c3bdfSopenharmony_ci * XXX Due to a possible kernel bug, oom-killer can be easily 130f08c3bdfSopenharmony_ci * triggered when doing small piece mmaps in huge amount even if 131f08c3bdfSopenharmony_ci * enough free memory available. Also it has been observed that 132f08c3bdfSopenharmony_ci * oom-killer often kill wrong victims in this situation, we 133f08c3bdfSopenharmony_ci * decided to do following steps to make sure no oom happen: 134f08c3bdfSopenharmony_ci * 1) use a safe maximum max_map_count value as upper-bound, 135f08c3bdfSopenharmony_ci * we set it 65536 in this case, i.e., we don't test too big 136f08c3bdfSopenharmony_ci * value; 137f08c3bdfSopenharmony_ci * 2) make sure total mapping isn't larger than 138f08c3bdfSopenharmony_ci * CommitLimit - Committed_AS 139f08c3bdfSopenharmony_ci */ 140f08c3bdfSopenharmony_ci memfree = SAFE_READ_MEMINFO("CommitLimit:") - SAFE_READ_MEMINFO("Committed_AS:"); 141f08c3bdfSopenharmony_ci /* 64 used as a bias to make sure no overflow happen */ 142f08c3bdfSopenharmony_ci max_iters = memfree / sysconf(_SC_PAGESIZE) * 1024 - 64; 143f08c3bdfSopenharmony_ci if (max_iters > MAX_MAP_COUNT) 144f08c3bdfSopenharmony_ci max_iters = MAX_MAP_COUNT; 145f08c3bdfSopenharmony_ci 146f08c3bdfSopenharmony_ci max_maps = MAP_COUNT_DEFAULT; 147f08c3bdfSopenharmony_ci if (max_iters < max_maps) 148f08c3bdfSopenharmony_ci tst_brk(TCONF, "test requires more free memory"); 149f08c3bdfSopenharmony_ci 150f08c3bdfSopenharmony_ci while (max_maps <= max_iters) { 151f08c3bdfSopenharmony_ci set_sys_tune("max_map_count", max_maps, 1); 152f08c3bdfSopenharmony_ci 153f08c3bdfSopenharmony_ci switch (pid = SAFE_FORK()) { 154f08c3bdfSopenharmony_ci case 0: 155f08c3bdfSopenharmony_ci while (mmap(NULL, 1, PROT_READ, 156f08c3bdfSopenharmony_ci MAP_SHARED | MAP_ANONYMOUS, -1, 0) 157f08c3bdfSopenharmony_ci != MAP_FAILED) ; 158f08c3bdfSopenharmony_ci if (raise(SIGSTOP) != 0) 159f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "raise"); 160f08c3bdfSopenharmony_ci exit(0); 161f08c3bdfSopenharmony_ci default: 162f08c3bdfSopenharmony_ci break; 163f08c3bdfSopenharmony_ci } 164f08c3bdfSopenharmony_ci /* wait child done mmap and stop */ 165f08c3bdfSopenharmony_ci SAFE_WAITPID(pid, &status, WUNTRACED); 166f08c3bdfSopenharmony_ci if (!WIFSTOPPED(status)) 167f08c3bdfSopenharmony_ci tst_brk(TBROK, "child did not stopped"); 168f08c3bdfSopenharmony_ci 169f08c3bdfSopenharmony_ci map_count = count_maps(pid); 170f08c3bdfSopenharmony_ci /* Note max_maps will be exceeded by one for 171f08c3bdfSopenharmony_ci * the sysctl setting of max_map_count. This 172f08c3bdfSopenharmony_ci * is the mm failure point at the time of 173f08c3bdfSopenharmony_ci * writing this COMMENT! 174f08c3bdfSopenharmony_ci */ 175f08c3bdfSopenharmony_ci if (map_count == (max_maps + 1)) 176f08c3bdfSopenharmony_ci tst_res(TPASS, "%ld map entries in total " 177f08c3bdfSopenharmony_ci "as expected.", max_maps); 178f08c3bdfSopenharmony_ci else 179f08c3bdfSopenharmony_ci tst_res(TFAIL, "%ld map entries in total, but " 180f08c3bdfSopenharmony_ci "expected %ld entries", map_count, max_maps); 181f08c3bdfSopenharmony_ci 182f08c3bdfSopenharmony_ci /* make child continue to exit */ 183f08c3bdfSopenharmony_ci SAFE_KILL(pid, SIGCONT); 184f08c3bdfSopenharmony_ci SAFE_WAITPID(pid, &status, 0); 185f08c3bdfSopenharmony_ci 186f08c3bdfSopenharmony_ci max_maps = max_maps << 1; 187f08c3bdfSopenharmony_ci } 188f08c3bdfSopenharmony_ci} 189f08c3bdfSopenharmony_ci 190f08c3bdfSopenharmony_cistatic struct tst_test test = { 191f08c3bdfSopenharmony_ci .needs_root = 1, 192f08c3bdfSopenharmony_ci .forks_child = 1, 193f08c3bdfSopenharmony_ci .test_all = max_map_count_test, 194f08c3bdfSopenharmony_ci .save_restore = (const struct tst_path_val[]) { 195f08c3bdfSopenharmony_ci {"/proc/sys/vm/overcommit_memory", "0", TST_SR_TBROK}, 196f08c3bdfSopenharmony_ci {"/proc/sys/vm/max_map_count", NULL, TST_SR_TBROK}, 197f08c3bdfSopenharmony_ci {} 198f08c3bdfSopenharmony_ci }, 199f08c3bdfSopenharmony_ci}; 200