1409437e1SDaniel P. Berrange /* 2409437e1SDaniel P. Berrange * Migration stress workload 3409437e1SDaniel P. Berrange * 4409437e1SDaniel P. Berrange * Copyright (c) 2016 Red Hat, Inc. 5409437e1SDaniel P. Berrange * 6409437e1SDaniel P. Berrange * This library is free software; you can redistribute it and/or 7409437e1SDaniel P. Berrange * modify it under the terms of the GNU Lesser General Public 8409437e1SDaniel P. Berrange * License as published by the Free Software Foundation; either 9409437e1SDaniel P. Berrange * version 2 of the License, or (at your option) any later version. 10409437e1SDaniel P. Berrange * 11409437e1SDaniel P. Berrange * This library is distributed in the hope that it will be useful, 12409437e1SDaniel P. Berrange * but WITHOUT ANY WARRANTY; without even the implied warranty of 13409437e1SDaniel P. Berrange * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14409437e1SDaniel P. Berrange * Lesser General Public License for more details. 15409437e1SDaniel P. Berrange * 16409437e1SDaniel P. Berrange * You should have received a copy of the GNU Lesser General Public 17409437e1SDaniel P. Berrange * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18409437e1SDaniel P. Berrange */ 19409437e1SDaniel P. Berrange 20*8f0a3716SMarkus Armbruster #include "qemu/osdep.h" 21409437e1SDaniel P. Berrange #include <getopt.h> 22409437e1SDaniel P. Berrange #include <sys/reboot.h> 23409437e1SDaniel P. Berrange #include <sys/syscall.h> 24409437e1SDaniel P. Berrange #include <linux/random.h> 25409437e1SDaniel P. Berrange #include <pthread.h> 26409437e1SDaniel P. Berrange #include <sys/mount.h> 27409437e1SDaniel P. Berrange 28409437e1SDaniel P. Berrange const char *argv0; 29409437e1SDaniel P. Berrange 30409437e1SDaniel P. Berrange #define PAGE_SIZE 4096 31409437e1SDaniel P. Berrange 32409437e1SDaniel P. Berrange static int gettid(void) 33409437e1SDaniel P. Berrange { 34409437e1SDaniel P. Berrange return syscall(SYS_gettid); 35409437e1SDaniel P. Berrange } 36409437e1SDaniel P. Berrange 37409437e1SDaniel P. Berrange static __attribute__((noreturn)) void exit_failure(void) 38409437e1SDaniel P. Berrange { 39409437e1SDaniel P. Berrange if (getpid() == 1) { 40409437e1SDaniel P. Berrange sync(); 41409437e1SDaniel P. Berrange reboot(RB_POWER_OFF); 42409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: cannot reboot: %s\n", 43409437e1SDaniel P. Berrange argv0, gettid(), strerror(errno)); 44409437e1SDaniel P. Berrange abort(); 45409437e1SDaniel P. Berrange } else { 46409437e1SDaniel P. Berrange exit(1); 47409437e1SDaniel P. Berrange } 48409437e1SDaniel P. Berrange } 49409437e1SDaniel P. Berrange 50409437e1SDaniel P. Berrange static __attribute__((noreturn)) void exit_success(void) 51409437e1SDaniel P. Berrange { 52409437e1SDaniel P. Berrange if (getpid() == 1) { 53409437e1SDaniel P. Berrange sync(); 54409437e1SDaniel P. Berrange reboot(RB_POWER_OFF); 55409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: cannot reboot: %s\n", 56409437e1SDaniel P. Berrange argv0, gettid(), strerror(errno)); 57409437e1SDaniel P. Berrange abort(); 58409437e1SDaniel P. Berrange } else { 59409437e1SDaniel P. Berrange exit(0); 60409437e1SDaniel P. Berrange } 61409437e1SDaniel P. Berrange } 62409437e1SDaniel P. Berrange 63409437e1SDaniel P. Berrange static int get_command_arg_str(const char *name, 64409437e1SDaniel P. Berrange char **val) 65409437e1SDaniel P. Berrange { 66409437e1SDaniel P. Berrange static char line[1024]; 67409437e1SDaniel P. Berrange FILE *fp = fopen("/proc/cmdline", "r"); 68409437e1SDaniel P. Berrange char *start, *end; 69409437e1SDaniel P. Berrange 70409437e1SDaniel P. Berrange if (fp == NULL) { 71409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: cannot open /proc/cmdline: %s\n", 72409437e1SDaniel P. Berrange argv0, gettid(), strerror(errno)); 73409437e1SDaniel P. Berrange return -1; 74409437e1SDaniel P. Berrange } 75409437e1SDaniel P. Berrange 76409437e1SDaniel P. Berrange if (!fgets(line, sizeof line, fp)) { 77409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: cannot read /proc/cmdline: %s\n", 78409437e1SDaniel P. Berrange argv0, gettid(), strerror(errno)); 79409437e1SDaniel P. Berrange fclose(fp); 80409437e1SDaniel P. Berrange return -1; 81409437e1SDaniel P. Berrange } 82409437e1SDaniel P. Berrange fclose(fp); 83409437e1SDaniel P. Berrange 84409437e1SDaniel P. Berrange start = strstr(line, name); 85409437e1SDaniel P. Berrange if (!start) 86409437e1SDaniel P. Berrange return 0; 87409437e1SDaniel P. Berrange 88409437e1SDaniel P. Berrange start += strlen(name); 89409437e1SDaniel P. Berrange 90409437e1SDaniel P. Berrange if (*start != '=') { 91409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n", 92409437e1SDaniel P. Berrange argv0, gettid(), name); 93409437e1SDaniel P. Berrange } 94409437e1SDaniel P. Berrange start++; 95409437e1SDaniel P. Berrange 96409437e1SDaniel P. Berrange end = strstr(start, " "); 97409437e1SDaniel P. Berrange if (!end) 98409437e1SDaniel P. Berrange end = strstr(start, "\n"); 99409437e1SDaniel P. Berrange 100409437e1SDaniel P. Berrange if (end == start) { 101409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n", 102409437e1SDaniel P. Berrange argv0, gettid(), name); 103409437e1SDaniel P. Berrange return -1; 104409437e1SDaniel P. Berrange } 105409437e1SDaniel P. Berrange 106409437e1SDaniel P. Berrange if (end) 107409437e1SDaniel P. Berrange *val = strndup(start, end - start); 108409437e1SDaniel P. Berrange else 109409437e1SDaniel P. Berrange *val = strdup(start); 110409437e1SDaniel P. Berrange return 1; 111409437e1SDaniel P. Berrange } 112409437e1SDaniel P. Berrange 113409437e1SDaniel P. Berrange 114409437e1SDaniel P. Berrange static int get_command_arg_ull(const char *name, 115409437e1SDaniel P. Berrange unsigned long long *val) 116409437e1SDaniel P. Berrange { 117409437e1SDaniel P. Berrange char *valstr; 118409437e1SDaniel P. Berrange char *end; 119409437e1SDaniel P. Berrange 120409437e1SDaniel P. Berrange int ret = get_command_arg_str(name, &valstr); 121409437e1SDaniel P. Berrange if (ret <= 0) 122409437e1SDaniel P. Berrange return ret; 123409437e1SDaniel P. Berrange 124409437e1SDaniel P. Berrange errno = 0; 125409437e1SDaniel P. Berrange *val = strtoll(valstr, &end, 10); 126409437e1SDaniel P. Berrange if (errno || *end) { 127409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: cannot parse %s value %s\n", 128409437e1SDaniel P. Berrange argv0, gettid(), name, valstr); 129409437e1SDaniel P. Berrange free(valstr); 130409437e1SDaniel P. Berrange return -1; 131409437e1SDaniel P. Berrange } 132409437e1SDaniel P. Berrange free(valstr); 133409437e1SDaniel P. Berrange return 0; 134409437e1SDaniel P. Berrange } 135409437e1SDaniel P. Berrange 136409437e1SDaniel P. Berrange 137409437e1SDaniel P. Berrange static int random_bytes(char *buf, size_t len) 138409437e1SDaniel P. Berrange { 139409437e1SDaniel P. Berrange int fd; 140409437e1SDaniel P. Berrange 141409437e1SDaniel P. Berrange fd = open("/dev/urandom", O_RDONLY); 142409437e1SDaniel P. Berrange if (fd < 0) { 143409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: cannot open /dev/urandom: %s\n", 144409437e1SDaniel P. Berrange argv0, gettid(), strerror(errno)); 145409437e1SDaniel P. Berrange return -1; 146409437e1SDaniel P. Berrange } 147409437e1SDaniel P. Berrange 148409437e1SDaniel P. Berrange if (read(fd, buf, len) != len) { 149409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: cannot read /dev/urandom: %s\n", 150409437e1SDaniel P. Berrange argv0, gettid(), strerror(errno)); 151409437e1SDaniel P. Berrange close(fd); 152409437e1SDaniel P. Berrange return -1; 153409437e1SDaniel P. Berrange } 154409437e1SDaniel P. Berrange 155409437e1SDaniel P. Berrange close(fd); 156409437e1SDaniel P. Berrange 157409437e1SDaniel P. Berrange return 0; 158409437e1SDaniel P. Berrange } 159409437e1SDaniel P. Berrange 160409437e1SDaniel P. Berrange 161409437e1SDaniel P. Berrange static unsigned long long now(void) 162409437e1SDaniel P. Berrange { 163409437e1SDaniel P. Berrange struct timeval tv; 164409437e1SDaniel P. Berrange 165409437e1SDaniel P. Berrange gettimeofday(&tv, NULL); 166409437e1SDaniel P. Berrange 167409437e1SDaniel P. Berrange return (tv.tv_sec * 1000ull) + (tv.tv_usec / 1000ull); 168409437e1SDaniel P. Berrange } 169409437e1SDaniel P. Berrange 170409437e1SDaniel P. Berrange static int stressone(unsigned long long ramsizeMB) 171409437e1SDaniel P. Berrange { 172409437e1SDaniel P. Berrange size_t pagesPerMB = 1024 * 1024 / PAGE_SIZE; 173409437e1SDaniel P. Berrange char *ram = malloc(ramsizeMB * 1024 * 1024); 174409437e1SDaniel P. Berrange char *ramptr; 175409437e1SDaniel P. Berrange size_t i, j, k; 176409437e1SDaniel P. Berrange char *data = malloc(PAGE_SIZE); 177409437e1SDaniel P. Berrange char *dataptr; 178409437e1SDaniel P. Berrange size_t nMB = 0; 179409437e1SDaniel P. Berrange unsigned long long before, after; 180409437e1SDaniel P. Berrange 181409437e1SDaniel P. Berrange if (!ram) { 182409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: cannot allocate %llu MB of RAM: %s\n", 183409437e1SDaniel P. Berrange argv0, gettid(), ramsizeMB, strerror(errno)); 184409437e1SDaniel P. Berrange return -1; 185409437e1SDaniel P. Berrange } 186409437e1SDaniel P. Berrange if (!data) { 187409437e1SDaniel P. Berrange fprintf(stderr, "%s (%d): ERROR: cannot allocate %d bytes of RAM: %s\n", 188409437e1SDaniel P. Berrange argv0, gettid(), PAGE_SIZE, strerror(errno)); 189409437e1SDaniel P. Berrange free(ram); 190409437e1SDaniel P. Berrange return -1; 191409437e1SDaniel P. Berrange } 192409437e1SDaniel P. Berrange 193409437e1SDaniel P. Berrange /* We don't care about initial state, but we do want 194409437e1SDaniel P. Berrange * to fault it all into RAM, otherwise the first iter 195409437e1SDaniel P. Berrange * of the loop below will be quite slow. We cna't use 196409437e1SDaniel P. Berrange * 0x0 as the byte as gcc optimizes that away into a 197409437e1SDaniel P. Berrange * calloc instead :-) */ 198409437e1SDaniel P. Berrange memset(ram, 0xfe, ramsizeMB * 1024 * 1024); 199409437e1SDaniel P. Berrange 200409437e1SDaniel P. Berrange if (random_bytes(data, PAGE_SIZE) < 0) { 201409437e1SDaniel P. Berrange free(ram); 202409437e1SDaniel P. Berrange free(data); 203409437e1SDaniel P. Berrange return -1; 204409437e1SDaniel P. Berrange } 205409437e1SDaniel P. Berrange 206409437e1SDaniel P. Berrange before = now(); 207409437e1SDaniel P. Berrange 208409437e1SDaniel P. Berrange while (1) { 209409437e1SDaniel P. Berrange 210409437e1SDaniel P. Berrange ramptr = ram; 211409437e1SDaniel P. Berrange for (i = 0; i < ramsizeMB; i++, nMB++) { 212409437e1SDaniel P. Berrange for (j = 0; j < pagesPerMB; j++) { 213409437e1SDaniel P. Berrange dataptr = data; 214409437e1SDaniel P. Berrange for (k = 0; k < PAGE_SIZE; k += sizeof(long long)) { 215409437e1SDaniel P. Berrange ramptr += sizeof(long long); 216409437e1SDaniel P. Berrange dataptr += sizeof(long long); 217409437e1SDaniel P. Berrange *(unsigned long long *)ramptr ^= *(unsigned long long *)dataptr; 218409437e1SDaniel P. Berrange } 219409437e1SDaniel P. Berrange } 220409437e1SDaniel P. Berrange 221409437e1SDaniel P. Berrange if (nMB == 1024) { 222409437e1SDaniel P. Berrange after = now(); 223409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): INFO: %06llums copied 1 GB in %05llums\n", 224409437e1SDaniel P. Berrange argv0, gettid(), after, after - before); 225409437e1SDaniel P. Berrange before = now(); 226409437e1SDaniel P. Berrange nMB = 0; 227409437e1SDaniel P. Berrange } 228409437e1SDaniel P. Berrange } 229409437e1SDaniel P. Berrange } 230409437e1SDaniel P. Berrange 231409437e1SDaniel P. Berrange free(data); 232409437e1SDaniel P. Berrange free(ram); 233409437e1SDaniel P. Berrange } 234409437e1SDaniel P. Berrange 235409437e1SDaniel P. Berrange 236409437e1SDaniel P. Berrange static void *stressthread(void *arg) 237409437e1SDaniel P. Berrange { 238409437e1SDaniel P. Berrange unsigned long long ramsizeMB = *(unsigned long long *)arg; 239409437e1SDaniel P. Berrange 240409437e1SDaniel P. Berrange stressone(ramsizeMB); 241409437e1SDaniel P. Berrange 242409437e1SDaniel P. Berrange return NULL; 243409437e1SDaniel P. Berrange } 244409437e1SDaniel P. Berrange 245409437e1SDaniel P. Berrange static int stress(unsigned long long ramsizeGB, int ncpus) 246409437e1SDaniel P. Berrange { 247409437e1SDaniel P. Berrange size_t i; 248409437e1SDaniel P. Berrange unsigned long long ramsizeMB = ramsizeGB * 1024 / ncpus; 249409437e1SDaniel P. Berrange ncpus--; 250409437e1SDaniel P. Berrange 251409437e1SDaniel P. Berrange for (i = 0; i < ncpus; i++) { 252409437e1SDaniel P. Berrange pthread_t thr; 253409437e1SDaniel P. Berrange pthread_create(&thr, NULL, 254409437e1SDaniel P. Berrange stressthread, &ramsizeMB); 255409437e1SDaniel P. Berrange } 256409437e1SDaniel P. Berrange 257409437e1SDaniel P. Berrange stressone(ramsizeMB); 258409437e1SDaniel P. Berrange 259409437e1SDaniel P. Berrange return 0; 260409437e1SDaniel P. Berrange } 261409437e1SDaniel P. Berrange 262409437e1SDaniel P. Berrange 263409437e1SDaniel P. Berrange static int mount_misc(const char *fstype, const char *dir) 264409437e1SDaniel P. Berrange { 265409437e1SDaniel P. Berrange if (mkdir(dir, 0755) < 0 && errno != EEXIST) { 266409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: cannot create %s: %s\n", 267409437e1SDaniel P. Berrange argv0, gettid(), dir, strerror(errno)); 268409437e1SDaniel P. Berrange return -1; 269409437e1SDaniel P. Berrange } 270409437e1SDaniel P. Berrange 271409437e1SDaniel P. Berrange if (mount("none", dir, fstype, 0, NULL) < 0) { 272409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: cannot mount %s: %s\n", 273409437e1SDaniel P. Berrange argv0, gettid(), dir, strerror(errno)); 274409437e1SDaniel P. Berrange return -1; 275409437e1SDaniel P. Berrange } 276409437e1SDaniel P. Berrange 277409437e1SDaniel P. Berrange return 0; 278409437e1SDaniel P. Berrange } 279409437e1SDaniel P. Berrange 280409437e1SDaniel P. Berrange static int mount_all(void) 281409437e1SDaniel P. Berrange { 282409437e1SDaniel P. Berrange if (mount_misc("proc", "/proc") < 0 || 283409437e1SDaniel P. Berrange mount_misc("sysfs", "/sys") < 0 || 284409437e1SDaniel P. Berrange mount_misc("tmpfs", "/dev") < 0) 285409437e1SDaniel P. Berrange return -1; 286409437e1SDaniel P. Berrange 287409437e1SDaniel P. Berrange mknod("/dev/urandom", 0777 | S_IFCHR, makedev(1, 9)); 288409437e1SDaniel P. Berrange mknod("/dev/random", 0777 | S_IFCHR, makedev(1, 8)); 289409437e1SDaniel P. Berrange 290409437e1SDaniel P. Berrange return 0; 291409437e1SDaniel P. Berrange } 292409437e1SDaniel P. Berrange 293409437e1SDaniel P. Berrange int main(int argc, char **argv) 294409437e1SDaniel P. Berrange { 295409437e1SDaniel P. Berrange unsigned long long ramsizeGB = 1; 296409437e1SDaniel P. Berrange char *end; 297409437e1SDaniel P. Berrange int ch; 298409437e1SDaniel P. Berrange int opt_ind = 0; 299409437e1SDaniel P. Berrange const char *sopt = "hr:c:"; 300409437e1SDaniel P. Berrange struct option lopt[] = { 301409437e1SDaniel P. Berrange { "help", no_argument, NULL, 'h' }, 302409437e1SDaniel P. Berrange { "ramsize", required_argument, NULL, 'r' }, 303409437e1SDaniel P. Berrange { "cpus", required_argument, NULL, 'c' }, 304409437e1SDaniel P. Berrange { NULL, 0, NULL, 0 } 305409437e1SDaniel P. Berrange }; 306409437e1SDaniel P. Berrange int ret; 307409437e1SDaniel P. Berrange int ncpus = 0; 308409437e1SDaniel P. Berrange 309409437e1SDaniel P. Berrange argv0 = argv[0]; 310409437e1SDaniel P. Berrange 311409437e1SDaniel P. Berrange while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { 312409437e1SDaniel P. Berrange switch (ch) { 313409437e1SDaniel P. Berrange case 'r': 314409437e1SDaniel P. Berrange errno = 0; 315409437e1SDaniel P. Berrange ramsizeGB = strtoll(optarg, &end, 10); 316409437e1SDaniel P. Berrange if (errno != 0 || *end) { 317409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: Cannot parse RAM size %s\n", 318409437e1SDaniel P. Berrange argv0, gettid(), optarg); 319409437e1SDaniel P. Berrange exit_failure(); 320409437e1SDaniel P. Berrange } 321409437e1SDaniel P. Berrange break; 322409437e1SDaniel P. Berrange 323409437e1SDaniel P. Berrange case 'c': 324409437e1SDaniel P. Berrange errno = 0; 325409437e1SDaniel P. Berrange ncpus = strtoll(optarg, &end, 10); 326409437e1SDaniel P. Berrange if (errno != 0 || *end) { 327409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: Cannot parse CPU count %s\n", 328409437e1SDaniel P. Berrange argv0, gettid(), optarg); 329409437e1SDaniel P. Berrange exit_failure(); 330409437e1SDaniel P. Berrange } 331409437e1SDaniel P. Berrange break; 332409437e1SDaniel P. Berrange 333409437e1SDaniel P. Berrange case '?': 334409437e1SDaniel P. Berrange case 'h': 335409437e1SDaniel P. Berrange fprintf(stderr, "%s: [--help][--ramsize GB][--cpus N]\n", argv0); 336409437e1SDaniel P. Berrange exit_failure(); 337409437e1SDaniel P. Berrange } 338409437e1SDaniel P. Berrange } 339409437e1SDaniel P. Berrange 340409437e1SDaniel P. Berrange if (getpid() == 1) { 341409437e1SDaniel P. Berrange if (mount_all() < 0) 342409437e1SDaniel P. Berrange exit_failure(); 343409437e1SDaniel P. Berrange 344409437e1SDaniel P. Berrange ret = get_command_arg_ull("ramsize", &ramsizeGB); 345409437e1SDaniel P. Berrange if (ret < 0) 346409437e1SDaniel P. Berrange exit_failure(); 347409437e1SDaniel P. Berrange } 348409437e1SDaniel P. Berrange 349409437e1SDaniel P. Berrange if (ncpus == 0) 350409437e1SDaniel P. Berrange ncpus = sysconf(_SC_NPROCESSORS_ONLN); 351409437e1SDaniel P. Berrange 352409437e1SDaniel P. Berrange fprintf(stdout, "%s (%05d): INFO: RAM %llu GiB across %d CPUs\n", 353409437e1SDaniel P. Berrange argv0, gettid(), ramsizeGB, ncpus); 354409437e1SDaniel P. Berrange 355409437e1SDaniel P. Berrange if (stress(ramsizeGB, ncpus) < 0) 356409437e1SDaniel P. Berrange exit_failure(); 357409437e1SDaniel P. Berrange 358409437e1SDaniel P. Berrange exit_success(); 359409437e1SDaniel P. Berrange } 360