1 /* 2 * Migration stress workload 3 * 4 * Copyright (c) 2016 Red Hat, Inc. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include <stdio.h> 21 #include <getopt.h> 22 #include <string.h> 23 #include <stdlib.h> 24 #include <errno.h> 25 #include <unistd.h> 26 #include <sys/reboot.h> 27 #include <sys/syscall.h> 28 #include <linux/random.h> 29 #include <sys/time.h> 30 #include <pthread.h> 31 #include <fcntl.h> 32 #include <sys/mount.h> 33 #include <sys/stat.h> 34 #include <sys/mman.h> 35 36 const char *argv0; 37 38 #define PAGE_SIZE 4096 39 40 static int gettid(void) 41 { 42 return syscall(SYS_gettid); 43 } 44 45 static __attribute__((noreturn)) void exit_failure(void) 46 { 47 if (getpid() == 1) { 48 sync(); 49 reboot(RB_POWER_OFF); 50 fprintf(stderr, "%s (%05d): ERROR: cannot reboot: %s\n", 51 argv0, gettid(), strerror(errno)); 52 abort(); 53 } else { 54 exit(1); 55 } 56 } 57 58 static __attribute__((noreturn)) void exit_success(void) 59 { 60 if (getpid() == 1) { 61 sync(); 62 reboot(RB_POWER_OFF); 63 fprintf(stderr, "%s (%05d): ERROR: cannot reboot: %s\n", 64 argv0, gettid(), strerror(errno)); 65 abort(); 66 } else { 67 exit(0); 68 } 69 } 70 71 static int get_command_arg_str(const char *name, 72 char **val) 73 { 74 static char line[1024]; 75 FILE *fp = fopen("/proc/cmdline", "r"); 76 char *start, *end; 77 78 if (fp == NULL) { 79 fprintf(stderr, "%s (%05d): ERROR: cannot open /proc/cmdline: %s\n", 80 argv0, gettid(), strerror(errno)); 81 return -1; 82 } 83 84 if (!fgets(line, sizeof line, fp)) { 85 fprintf(stderr, "%s (%05d): ERROR: cannot read /proc/cmdline: %s\n", 86 argv0, gettid(), strerror(errno)); 87 fclose(fp); 88 return -1; 89 } 90 fclose(fp); 91 92 start = strstr(line, name); 93 if (!start) 94 return 0; 95 96 start += strlen(name); 97 98 if (*start != '=') { 99 fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n", 100 argv0, gettid(), name); 101 } 102 start++; 103 104 end = strstr(start, " "); 105 if (!end) 106 end = strstr(start, "\n"); 107 108 if (end == start) { 109 fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n", 110 argv0, gettid(), name); 111 return -1; 112 } 113 114 if (end) 115 *val = strndup(start, end - start); 116 else 117 *val = strdup(start); 118 return 1; 119 } 120 121 122 static int get_command_arg_ull(const char *name, 123 unsigned long long *val) 124 { 125 char *valstr; 126 char *end; 127 128 int ret = get_command_arg_str(name, &valstr); 129 if (ret <= 0) 130 return ret; 131 132 errno = 0; 133 *val = strtoll(valstr, &end, 10); 134 if (errno || *end) { 135 fprintf(stderr, "%s (%05d): ERROR: cannot parse %s value %s\n", 136 argv0, gettid(), name, valstr); 137 free(valstr); 138 return -1; 139 } 140 free(valstr); 141 return 0; 142 } 143 144 145 static int random_bytes(char *buf, size_t len) 146 { 147 int fd; 148 149 fd = open("/dev/urandom", O_RDONLY); 150 if (fd < 0) { 151 fprintf(stderr, "%s (%05d): ERROR: cannot open /dev/urandom: %s\n", 152 argv0, gettid(), strerror(errno)); 153 return -1; 154 } 155 156 if (read(fd, buf, len) != len) { 157 fprintf(stderr, "%s (%05d): ERROR: cannot read /dev/urandom: %s\n", 158 argv0, gettid(), strerror(errno)); 159 close(fd); 160 return -1; 161 } 162 163 close(fd); 164 165 return 0; 166 } 167 168 169 static unsigned long long now(void) 170 { 171 struct timeval tv; 172 173 gettimeofday(&tv, NULL); 174 175 return (tv.tv_sec * 1000ull) + (tv.tv_usec / 1000ull); 176 } 177 178 static int stressone(unsigned long long ramsizeMB) 179 { 180 size_t pagesPerMB = 1024 * 1024 / PAGE_SIZE; 181 char *ram = malloc(ramsizeMB * 1024 * 1024); 182 char *ramptr; 183 size_t i, j, k; 184 char *data = malloc(PAGE_SIZE); 185 char *dataptr; 186 size_t nMB = 0; 187 unsigned long long before, after; 188 189 if (!ram) { 190 fprintf(stderr, "%s (%05d): ERROR: cannot allocate %llu MB of RAM: %s\n", 191 argv0, gettid(), ramsizeMB, strerror(errno)); 192 return -1; 193 } 194 if (!data) { 195 fprintf(stderr, "%s (%d): ERROR: cannot allocate %d bytes of RAM: %s\n", 196 argv0, gettid(), PAGE_SIZE, strerror(errno)); 197 free(ram); 198 return -1; 199 } 200 201 /* We don't care about initial state, but we do want 202 * to fault it all into RAM, otherwise the first iter 203 * of the loop below will be quite slow. We cna't use 204 * 0x0 as the byte as gcc optimizes that away into a 205 * calloc instead :-) */ 206 memset(ram, 0xfe, ramsizeMB * 1024 * 1024); 207 208 if (random_bytes(data, PAGE_SIZE) < 0) { 209 free(ram); 210 free(data); 211 return -1; 212 } 213 214 before = now(); 215 216 while (1) { 217 218 ramptr = ram; 219 for (i = 0; i < ramsizeMB; i++, nMB++) { 220 for (j = 0; j < pagesPerMB; j++) { 221 dataptr = data; 222 for (k = 0; k < PAGE_SIZE; k += sizeof(long long)) { 223 ramptr += sizeof(long long); 224 dataptr += sizeof(long long); 225 *(unsigned long long *)ramptr ^= *(unsigned long long *)dataptr; 226 } 227 } 228 229 if (nMB == 1024) { 230 after = now(); 231 fprintf(stderr, "%s (%05d): INFO: %06llums copied 1 GB in %05llums\n", 232 argv0, gettid(), after, after - before); 233 before = now(); 234 nMB = 0; 235 } 236 } 237 } 238 239 free(data); 240 free(ram); 241 } 242 243 244 static void *stressthread(void *arg) 245 { 246 unsigned long long ramsizeMB = *(unsigned long long *)arg; 247 248 stressone(ramsizeMB); 249 250 return NULL; 251 } 252 253 static int stress(unsigned long long ramsizeGB, int ncpus) 254 { 255 size_t i; 256 unsigned long long ramsizeMB = ramsizeGB * 1024 / ncpus; 257 ncpus--; 258 259 for (i = 0; i < ncpus; i++) { 260 pthread_t thr; 261 pthread_create(&thr, NULL, 262 stressthread, &ramsizeMB); 263 } 264 265 stressone(ramsizeMB); 266 267 return 0; 268 } 269 270 271 static int mount_misc(const char *fstype, const char *dir) 272 { 273 if (mkdir(dir, 0755) < 0 && errno != EEXIST) { 274 fprintf(stderr, "%s (%05d): ERROR: cannot create %s: %s\n", 275 argv0, gettid(), dir, strerror(errno)); 276 return -1; 277 } 278 279 if (mount("none", dir, fstype, 0, NULL) < 0) { 280 fprintf(stderr, "%s (%05d): ERROR: cannot mount %s: %s\n", 281 argv0, gettid(), dir, strerror(errno)); 282 return -1; 283 } 284 285 return 0; 286 } 287 288 static int mount_all(void) 289 { 290 if (mount_misc("proc", "/proc") < 0 || 291 mount_misc("sysfs", "/sys") < 0 || 292 mount_misc("tmpfs", "/dev") < 0) 293 return -1; 294 295 mknod("/dev/urandom", 0777 | S_IFCHR, makedev(1, 9)); 296 mknod("/dev/random", 0777 | S_IFCHR, makedev(1, 8)); 297 298 return 0; 299 } 300 301 int main(int argc, char **argv) 302 { 303 unsigned long long ramsizeGB = 1; 304 char *end; 305 int ch; 306 int opt_ind = 0; 307 const char *sopt = "hr:c:"; 308 struct option lopt[] = { 309 { "help", no_argument, NULL, 'h' }, 310 { "ramsize", required_argument, NULL, 'r' }, 311 { "cpus", required_argument, NULL, 'c' }, 312 { NULL, 0, NULL, 0 } 313 }; 314 int ret; 315 int ncpus = 0; 316 317 argv0 = argv[0]; 318 319 while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { 320 switch (ch) { 321 case 'r': 322 errno = 0; 323 ramsizeGB = strtoll(optarg, &end, 10); 324 if (errno != 0 || *end) { 325 fprintf(stderr, "%s (%05d): ERROR: Cannot parse RAM size %s\n", 326 argv0, gettid(), optarg); 327 exit_failure(); 328 } 329 break; 330 331 case 'c': 332 errno = 0; 333 ncpus = strtoll(optarg, &end, 10); 334 if (errno != 0 || *end) { 335 fprintf(stderr, "%s (%05d): ERROR: Cannot parse CPU count %s\n", 336 argv0, gettid(), optarg); 337 exit_failure(); 338 } 339 break; 340 341 case '?': 342 case 'h': 343 fprintf(stderr, "%s: [--help][--ramsize GB][--cpus N]\n", argv0); 344 exit_failure(); 345 } 346 } 347 348 if (getpid() == 1) { 349 if (mount_all() < 0) 350 exit_failure(); 351 352 ret = get_command_arg_ull("ramsize", &ramsizeGB); 353 if (ret < 0) 354 exit_failure(); 355 } 356 357 if (ncpus == 0) 358 ncpus = sysconf(_SC_NPROCESSORS_ONLN); 359 360 fprintf(stdout, "%s (%05d): INFO: RAM %llu GiB across %d CPUs\n", 361 argv0, gettid(), ramsizeGB, ncpus); 362 363 if (stress(ramsizeGB, ncpus) < 0) 364 exit_failure(); 365 366 exit_success(); 367 } 368