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 208f0a3716SMarkus 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) 107348fbd58Stony.nguyen@bt.com *val = g_strndup(start, end - start); 108409437e1SDaniel P. Berrange else 109348fbd58Stony.nguyen@bt.com *val = g_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); 129348fbd58Stony.nguyen@bt.com g_free(valstr); 130409437e1SDaniel P. Berrange return -1; 131409437e1SDaniel P. Berrange } 132348fbd58Stony.nguyen@bt.com g_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; 173*f663492fSMao Zhongyi g_autofree char *ram = g_malloc(ramsizeMB * 1024 * 1024); 174409437e1SDaniel P. Berrange char *ramptr; 175409437e1SDaniel P. Berrange size_t i, j, k; 176*f663492fSMao Zhongyi g_autofree char *data = g_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 /* We don't care about initial state, but we do want 182409437e1SDaniel P. Berrange * to fault it all into RAM, otherwise the first iter 18381864c2eSMao Zhongyi * of the loop below will be quite slow. We can't use 184409437e1SDaniel P. Berrange * 0x0 as the byte as gcc optimizes that away into a 185409437e1SDaniel P. Berrange * calloc instead :-) */ 186409437e1SDaniel P. Berrange memset(ram, 0xfe, ramsizeMB * 1024 * 1024); 187409437e1SDaniel P. Berrange 188409437e1SDaniel P. Berrange if (random_bytes(data, PAGE_SIZE) < 0) { 189409437e1SDaniel P. Berrange return -1; 190409437e1SDaniel P. Berrange } 191409437e1SDaniel P. Berrange 192409437e1SDaniel P. Berrange before = now(); 193409437e1SDaniel P. Berrange 194409437e1SDaniel P. Berrange while (1) { 195409437e1SDaniel P. Berrange 196409437e1SDaniel P. Berrange ramptr = ram; 197409437e1SDaniel P. Berrange for (i = 0; i < ramsizeMB; i++, nMB++) { 198409437e1SDaniel P. Berrange for (j = 0; j < pagesPerMB; j++) { 199409437e1SDaniel P. Berrange dataptr = data; 200409437e1SDaniel P. Berrange for (k = 0; k < PAGE_SIZE; k += sizeof(long long)) { 201409437e1SDaniel P. Berrange ramptr += sizeof(long long); 202409437e1SDaniel P. Berrange dataptr += sizeof(long long); 203409437e1SDaniel P. Berrange *(unsigned long long *)ramptr ^= *(unsigned long long *)dataptr; 204409437e1SDaniel P. Berrange } 205409437e1SDaniel P. Berrange } 206409437e1SDaniel P. Berrange 207409437e1SDaniel P. Berrange if (nMB == 1024) { 208409437e1SDaniel P. Berrange after = now(); 209409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): INFO: %06llums copied 1 GB in %05llums\n", 210409437e1SDaniel P. Berrange argv0, gettid(), after, after - before); 211409437e1SDaniel P. Berrange before = now(); 212409437e1SDaniel P. Berrange nMB = 0; 213409437e1SDaniel P. Berrange } 214409437e1SDaniel P. Berrange } 215409437e1SDaniel P. Berrange } 216409437e1SDaniel P. Berrange } 217409437e1SDaniel P. Berrange 218409437e1SDaniel P. Berrange 219409437e1SDaniel P. Berrange static void *stressthread(void *arg) 220409437e1SDaniel P. Berrange { 221409437e1SDaniel P. Berrange unsigned long long ramsizeMB = *(unsigned long long *)arg; 222409437e1SDaniel P. Berrange 223409437e1SDaniel P. Berrange stressone(ramsizeMB); 224409437e1SDaniel P. Berrange 225409437e1SDaniel P. Berrange return NULL; 226409437e1SDaniel P. Berrange } 227409437e1SDaniel P. Berrange 228409437e1SDaniel P. Berrange static int stress(unsigned long long ramsizeGB, int ncpus) 229409437e1SDaniel P. Berrange { 230409437e1SDaniel P. Berrange size_t i; 231409437e1SDaniel P. Berrange unsigned long long ramsizeMB = ramsizeGB * 1024 / ncpus; 232409437e1SDaniel P. Berrange ncpus--; 233409437e1SDaniel P. Berrange 234409437e1SDaniel P. Berrange for (i = 0; i < ncpus; i++) { 235409437e1SDaniel P. Berrange pthread_t thr; 236409437e1SDaniel P. Berrange pthread_create(&thr, NULL, 237409437e1SDaniel P. Berrange stressthread, &ramsizeMB); 238409437e1SDaniel P. Berrange } 239409437e1SDaniel P. Berrange 240409437e1SDaniel P. Berrange stressone(ramsizeMB); 241409437e1SDaniel P. Berrange 242409437e1SDaniel P. Berrange return 0; 243409437e1SDaniel P. Berrange } 244409437e1SDaniel P. Berrange 245409437e1SDaniel P. Berrange 246409437e1SDaniel P. Berrange static int mount_misc(const char *fstype, const char *dir) 247409437e1SDaniel P. Berrange { 248409437e1SDaniel P. Berrange if (mkdir(dir, 0755) < 0 && errno != EEXIST) { 249409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: cannot create %s: %s\n", 250409437e1SDaniel P. Berrange argv0, gettid(), dir, strerror(errno)); 251409437e1SDaniel P. Berrange return -1; 252409437e1SDaniel P. Berrange } 253409437e1SDaniel P. Berrange 254409437e1SDaniel P. Berrange if (mount("none", dir, fstype, 0, NULL) < 0) { 255409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: cannot mount %s: %s\n", 256409437e1SDaniel P. Berrange argv0, gettid(), dir, strerror(errno)); 257409437e1SDaniel P. Berrange return -1; 258409437e1SDaniel P. Berrange } 259409437e1SDaniel P. Berrange 260409437e1SDaniel P. Berrange return 0; 261409437e1SDaniel P. Berrange } 262409437e1SDaniel P. Berrange 263409437e1SDaniel P. Berrange static int mount_all(void) 264409437e1SDaniel P. Berrange { 265409437e1SDaniel P. Berrange if (mount_misc("proc", "/proc") < 0 || 266409437e1SDaniel P. Berrange mount_misc("sysfs", "/sys") < 0 || 267409437e1SDaniel P. Berrange mount_misc("tmpfs", "/dev") < 0) 268409437e1SDaniel P. Berrange return -1; 269409437e1SDaniel P. Berrange 270409437e1SDaniel P. Berrange mknod("/dev/urandom", 0777 | S_IFCHR, makedev(1, 9)); 271409437e1SDaniel P. Berrange mknod("/dev/random", 0777 | S_IFCHR, makedev(1, 8)); 272409437e1SDaniel P. Berrange 273409437e1SDaniel P. Berrange return 0; 274409437e1SDaniel P. Berrange } 275409437e1SDaniel P. Berrange 276409437e1SDaniel P. Berrange int main(int argc, char **argv) 277409437e1SDaniel P. Berrange { 278409437e1SDaniel P. Berrange unsigned long long ramsizeGB = 1; 279409437e1SDaniel P. Berrange char *end; 280409437e1SDaniel P. Berrange int ch; 281409437e1SDaniel P. Berrange int opt_ind = 0; 282409437e1SDaniel P. Berrange const char *sopt = "hr:c:"; 283409437e1SDaniel P. Berrange struct option lopt[] = { 284409437e1SDaniel P. Berrange { "help", no_argument, NULL, 'h' }, 285409437e1SDaniel P. Berrange { "ramsize", required_argument, NULL, 'r' }, 286409437e1SDaniel P. Berrange { "cpus", required_argument, NULL, 'c' }, 287409437e1SDaniel P. Berrange { NULL, 0, NULL, 0 } 288409437e1SDaniel P. Berrange }; 289409437e1SDaniel P. Berrange int ret; 290409437e1SDaniel P. Berrange int ncpus = 0; 291409437e1SDaniel P. Berrange 292409437e1SDaniel P. Berrange argv0 = argv[0]; 293409437e1SDaniel P. Berrange 294409437e1SDaniel P. Berrange while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { 295409437e1SDaniel P. Berrange switch (ch) { 296409437e1SDaniel P. Berrange case 'r': 297409437e1SDaniel P. Berrange errno = 0; 298409437e1SDaniel P. Berrange ramsizeGB = strtoll(optarg, &end, 10); 299409437e1SDaniel P. Berrange if (errno != 0 || *end) { 300409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: Cannot parse RAM size %s\n", 301409437e1SDaniel P. Berrange argv0, gettid(), optarg); 302409437e1SDaniel P. Berrange exit_failure(); 303409437e1SDaniel P. Berrange } 304409437e1SDaniel P. Berrange break; 305409437e1SDaniel P. Berrange 306409437e1SDaniel P. Berrange case 'c': 307409437e1SDaniel P. Berrange errno = 0; 308409437e1SDaniel P. Berrange ncpus = strtoll(optarg, &end, 10); 309409437e1SDaniel P. Berrange if (errno != 0 || *end) { 310409437e1SDaniel P. Berrange fprintf(stderr, "%s (%05d): ERROR: Cannot parse CPU count %s\n", 311409437e1SDaniel P. Berrange argv0, gettid(), optarg); 312409437e1SDaniel P. Berrange exit_failure(); 313409437e1SDaniel P. Berrange } 314409437e1SDaniel P. Berrange break; 315409437e1SDaniel P. Berrange 316409437e1SDaniel P. Berrange case '?': 317409437e1SDaniel P. Berrange case 'h': 318409437e1SDaniel P. Berrange fprintf(stderr, "%s: [--help][--ramsize GB][--cpus N]\n", argv0); 319409437e1SDaniel P. Berrange exit_failure(); 320409437e1SDaniel P. Berrange } 321409437e1SDaniel P. Berrange } 322409437e1SDaniel P. Berrange 323409437e1SDaniel P. Berrange if (getpid() == 1) { 324409437e1SDaniel P. Berrange if (mount_all() < 0) 325409437e1SDaniel P. Berrange exit_failure(); 326409437e1SDaniel P. Berrange 327409437e1SDaniel P. Berrange ret = get_command_arg_ull("ramsize", &ramsizeGB); 328409437e1SDaniel P. Berrange if (ret < 0) 329409437e1SDaniel P. Berrange exit_failure(); 330409437e1SDaniel P. Berrange } 331409437e1SDaniel P. Berrange 332409437e1SDaniel P. Berrange if (ncpus == 0) 333409437e1SDaniel P. Berrange ncpus = sysconf(_SC_NPROCESSORS_ONLN); 334409437e1SDaniel P. Berrange 335409437e1SDaniel P. Berrange fprintf(stdout, "%s (%05d): INFO: RAM %llu GiB across %d CPUs\n", 336409437e1SDaniel P. Berrange argv0, gettid(), ramsizeGB, ncpus); 337409437e1SDaniel P. Berrange 338409437e1SDaniel P. Berrange if (stress(ramsizeGB, ncpus) < 0) 339409437e1SDaniel P. Berrange exit_failure(); 340409437e1SDaniel P. Berrange 341409437e1SDaniel P. Berrange exit_success(); 342409437e1SDaniel P. Berrange } 343