1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) International Business Machines Corp., 2001 4 * Copyright (c) Linux Test Project., 2019 5 * 6 * DESCRIPTION: 7 * 8 * mtest01 mallocs memory <chunksize> at a time until malloc fails. 9 * 10 * Parent process starts several child processes (each child process is 11 * tasked with allocating some amount of memory), it waits until all child 12 * processes send SIGRTMIN signal and resumes all children by sending the 13 * SIGCONT signal. 14 * 15 * Child process allocates certain amount of memory and fills it with some 16 * data (the '-w' option) so the pages are actually allocated when the desired 17 * amount of memory is allocated then it sends SIGRTMIN signal to the parent 18 * process, it pauses itself by raise SIGSTOP until get parent SIGCONT signal 19 * to continue and exit. 20 */ 21 22#include <sys/types.h> 23#include <sys/sysinfo.h> 24#include <sys/wait.h> 25#include <limits.h> 26#include <signal.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <unistd.h> 30 31#include "lapi/abisize.h" 32#include "tst_test.h" 33 34#define FIVE_HUNDRED_MB (500ULL*1024*1024) 35 36#if defined(__s390__) || defined(__s390x__) 37#define ALLOC_THRESHOLD FIVE_HUNDRED_MB 38#elif defined(TST_ABI32) 39#define ALLOC_THRESHOLD (2*FIVE_HUNDRED_MB) 40#elif defined(TST_ABI64) 41#define ALLOC_THRESHOLD (6*FIVE_HUNDRED_MB) 42#endif 43 44static pid_t *pid_list; 45static sig_atomic_t children_done; 46static int max_pids; 47static unsigned long long alloc_maxbytes; 48 49static int chunksize = 1024*1024; 50static int maxpercent = 20; 51static long maxbytes = 0; 52static char *dowrite; 53static char *verbose; 54 55static char *opt_chunksize, *opt_maxbytes, *opt_maxpercent; 56 57static void parse_mtest_options(char *str_chunksize, int *chunksize, 58 char *str_maxbytes, long *maxbytes, 59 char *str_maxpercent, int *maxpercent) 60{ 61 if (str_chunksize) 62 if (tst_parse_int(str_chunksize, chunksize, 1, INT_MAX)) 63 tst_brk(TBROK, "Invalid chunksize '%s'", str_chunksize); 64 65 if (str_maxbytes) { 66 if (tst_parse_long(str_maxbytes, maxbytes, 1, LONG_MAX)) { 67 tst_brk(TBROK, "Invalid maxbytes '%s'", str_maxbytes); 68 } else if (str_maxpercent) { 69 tst_brk(TBROK, "ERROR: -b option cannot be used with -p " 70 "option at the same time"); 71 } 72 alloc_maxbytes = (unsigned long long)maxbytes; 73 } 74 75 if (str_maxpercent) { 76 if (tst_parse_int(str_maxpercent, maxpercent, 1, 99)) { 77 tst_brk(TBROK, "Invalid maxpercent '%s'", str_maxpercent); 78 } else if (str_maxbytes) { 79 tst_brk(TBROK, "ERROR: -p option cannot be used with -b " 80 "option at the same time"); 81 } 82 } 83} 84 85static void handler(int sig LTP_ATTRIBUTE_UNUSED) 86{ 87 children_done++; 88} 89 90static void do_write_mem(char *mem, int chunksize) 91{ 92 int i, pagesz = getpagesize(); 93 94 for (i = 0; i < chunksize; i += pagesz) 95 *(mem + i) = 'a'; 96} 97 98static void setup(void) 99{ 100 struct sysinfo sstats; 101 unsigned long long total_free; 102 103 struct sigaction act; 104 act.sa_handler = handler; 105 act.sa_flags = 0; 106 sigemptyset(&act.sa_mask); 107 sigaction(SIGRTMIN, &act, 0); 108 109 parse_mtest_options(opt_chunksize, &chunksize, 110 opt_maxbytes, &maxbytes, 111 opt_maxpercent, &maxpercent); 112 sysinfo(&sstats); 113 total_free = sstats.freeram; 114 115 max_pids = total_free * sstats.mem_unit 116 / (unsigned long)ALLOC_THRESHOLD + 10; 117 pid_list = SAFE_MALLOC(max_pids * sizeof(pid_t)); 118 119 if (!alloc_maxbytes) { 120 /* set alloc_maxbytes to the extra amount we want to allocate */ 121 alloc_maxbytes = ((float)maxpercent / 100.00) 122 * (sstats.mem_unit * total_free); 123 tst_res(TINFO, "Filling up %d%% of free ram which is %llu kbytes", 124 maxpercent, alloc_maxbytes / 1024); 125 } 126} 127 128static void cleanup(void) 129{ 130 if(pid_list) 131 free(pid_list); 132} 133 134static void child_loop_alloc(unsigned long long alloc_bytes) 135{ 136 unsigned long bytecount = 0; 137 char *mem; 138 int runtime_rem; 139 140 tst_res(TINFO, "... child %d starting", getpid()); 141 142 while (1) { 143 mem = SAFE_MALLOC(chunksize); 144 if (dowrite) 145 do_write_mem(mem, chunksize); 146 147 if (verbose) 148 tst_res(TINFO, 149 "child %d allocated %lu bytes chunksize is %d", 150 getpid(), bytecount, chunksize); 151 bytecount += chunksize; 152 if (bytecount >= alloc_bytes) 153 break; 154 } 155 156 runtime_rem = tst_remaining_runtime(); 157 158 if (dowrite) 159 tst_res(TINFO, "... [t=%d] %lu bytes allocated and used in child %d", 160 runtime_rem, bytecount, getpid()); 161 else 162 tst_res(TINFO, "... [t=%d] %lu bytes allocated only in child %d", 163 runtime_rem, bytecount, getpid()); 164 165 kill(getppid(), SIGRTMIN); 166 raise(SIGSTOP); 167 exit(0); 168} 169 170static void mem_test(void) 171{ 172 pid_t pid; 173 int i = 0, pid_cntr = 0; 174 unsigned long long alloc_bytes = alloc_maxbytes; 175 const char *write_msg = ""; 176 177 if (dowrite) 178 write_msg = "(and written to) "; 179 180 /* to make mtest01 support -i N */ 181 children_done = 0; 182 183 do { 184 pid = SAFE_FORK(); 185 if (pid == 0) { 186 alloc_bytes = MIN(ALLOC_THRESHOLD, alloc_bytes); 187 child_loop_alloc(alloc_bytes); 188 } 189 190 pid_list[pid_cntr++] = pid; 191 192 if (alloc_bytes <= ALLOC_THRESHOLD) 193 break; 194 195 alloc_bytes -= ALLOC_THRESHOLD; 196 } while (pid_cntr < max_pids); 197 198 /* wait in the loop for all children finish allocating */ 199 while (children_done < pid_cntr) { 200 if (!tst_remaining_runtime()) { 201 tst_res(TWARN, 202 "the remaininig time is not enough for testing"); 203 break; 204 } 205 206 usleep(100000); 207 } 208 209 if (children_done < pid_cntr) { 210 tst_res(TFAIL, "kbytes allocated %sless than expected %llu", 211 write_msg, alloc_maxbytes / 1024); 212 213 for (i = 0; i < pid_cntr; i++) 214 kill(pid_list[i], SIGKILL); 215 216 return; 217 } 218 219 tst_res(TPASS, "%llu kbytes allocated %s", 220 alloc_maxbytes / 1024, write_msg); 221 222 for (i = 0; i < pid_cntr; i++) { 223 TST_PROCESS_STATE_WAIT(pid_list[i], 'T', 0); 224 kill(pid_list[i], SIGCONT); 225 } 226} 227 228static struct tst_test test = { 229 .forks_child = 1, 230 .options = (struct tst_option[]) { 231 {"c:", &opt_chunksize, "Size of chunk in bytes to malloc on each pass"}, 232 {"b:", &opt_maxbytes, "Maximum number of bytes to allocate before stopping"}, 233 {"p:", &opt_maxpercent, "Percent of total memory used at which the program stops"}, 234 {"w", &dowrite, "Write to the memory after allocating"}, 235 {"v", &verbose, "Verbose"}, 236 {} 237 }, 238 .max_runtime = 300, 239 .setup = setup, 240 .cleanup = cleanup, 241 .test_all = mem_test, 242}; 243