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