qemu/tests/migration/stress.c
<<
>>
Prefs
   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
  36const char *argv0;
  37
  38#define PAGE_SIZE 4096
  39
  40static int gettid(void)
  41{
  42    return syscall(SYS_gettid);
  43}
  44
  45static __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
  58static __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
  71static 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
 122static 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
 145static 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
 169static 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
 178static 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
 244static void *stressthread(void *arg)
 245{
 246    unsigned long long ramsizeMB = *(unsigned long long *)arg;
 247
 248    stressone(ramsizeMB);
 249
 250    return NULL;
 251}
 252
 253static 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
 271static 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
 288static 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
 301int 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