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 "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
  28const char *argv0;
  29
  30#define PAGE_SIZE 4096
  31
  32static int gettid(void)
  33{
  34    return syscall(SYS_gettid);
  35}
  36
  37static __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
  50static __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
  63static 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 = strndup(start, end - start);
 108    else
 109        *val = strdup(start);
 110    return 1;
 111}
 112
 113
 114static 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        free(valstr);
 130        return -1;
 131    }
 132    free(valstr);
 133    return 0;
 134}
 135
 136
 137static 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
 161static 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
 170static 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 cna'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
 236static void *stressthread(void *arg)
 237{
 238    unsigned long long ramsizeMB = *(unsigned long long *)arg;
 239
 240    stressone(ramsizeMB);
 241
 242    return NULL;
 243}
 244
 245static 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
 263static 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
 280static 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
 293int 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