There never was a "Lesser GPL version 2.0", It is either "GPL version 2.0" or "Lesser GPL version 2.1". This patch replaces all "Lesser GPL version 2.0" with "Lesser GPL version 2.1" in the tests/migration folder. Signed-off-by: Gan Qixin <ganqixin@huawei.com> Message-Id: <20201110184223.549499-2-ganqixin@huawei.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
329 lines
8.1 KiB
C
329 lines
8.1 KiB
C
/*
|
|
* Migration stress workload
|
|
*
|
|
* Copyright (c) 2016 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include <getopt.h>
|
|
#include <sys/reboot.h>
|
|
#include <sys/syscall.h>
|
|
#include <linux/random.h>
|
|
#include <pthread.h>
|
|
#include <sys/mount.h>
|
|
|
|
const char *argv0;
|
|
|
|
#define PAGE_SIZE 4096
|
|
|
|
#ifndef CONFIG_GETTID
|
|
static int gettid(void)
|
|
{
|
|
return syscall(SYS_gettid);
|
|
}
|
|
#endif
|
|
|
|
static __attribute__((noreturn)) void exit_failure(void)
|
|
{
|
|
if (getpid() == 1) {
|
|
sync();
|
|
reboot(RB_POWER_OFF);
|
|
fprintf(stderr, "%s (%05d): ERROR: cannot reboot: %s\n",
|
|
argv0, gettid(), strerror(errno));
|
|
abort();
|
|
} else {
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static int get_command_arg_str(const char *name,
|
|
char **val)
|
|
{
|
|
static char line[1024];
|
|
FILE *fp = fopen("/proc/cmdline", "r");
|
|
char *start, *end;
|
|
|
|
if (fp == NULL) {
|
|
fprintf(stderr, "%s (%05d): ERROR: cannot open /proc/cmdline: %s\n",
|
|
argv0, gettid(), strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (!fgets(line, sizeof line, fp)) {
|
|
fprintf(stderr, "%s (%05d): ERROR: cannot read /proc/cmdline: %s\n",
|
|
argv0, gettid(), strerror(errno));
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
fclose(fp);
|
|
|
|
start = strstr(line, name);
|
|
if (!start)
|
|
return 0;
|
|
|
|
start += strlen(name);
|
|
|
|
if (*start != '=') {
|
|
fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n",
|
|
argv0, gettid(), name);
|
|
}
|
|
start++;
|
|
|
|
end = strstr(start, " ");
|
|
if (!end)
|
|
end = strstr(start, "\n");
|
|
|
|
if (end == start) {
|
|
fprintf(stderr, "%s (%05d): ERROR: no value provided for '%s' in /proc/cmdline\n",
|
|
argv0, gettid(), name);
|
|
return -1;
|
|
}
|
|
|
|
if (end)
|
|
*val = g_strndup(start, end - start);
|
|
else
|
|
*val = g_strdup(start);
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int get_command_arg_ull(const char *name,
|
|
unsigned long long *val)
|
|
{
|
|
char *valstr;
|
|
char *end;
|
|
|
|
int ret = get_command_arg_str(name, &valstr);
|
|
if (ret <= 0)
|
|
return ret;
|
|
|
|
errno = 0;
|
|
*val = strtoll(valstr, &end, 10);
|
|
if (errno || *end) {
|
|
fprintf(stderr, "%s (%05d): ERROR: cannot parse %s value %s\n",
|
|
argv0, gettid(), name, valstr);
|
|
g_free(valstr);
|
|
return -1;
|
|
}
|
|
g_free(valstr);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int random_bytes(char *buf, size_t len)
|
|
{
|
|
int fd;
|
|
|
|
fd = open("/dev/urandom", O_RDONLY);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "%s (%05d): ERROR: cannot open /dev/urandom: %s\n",
|
|
argv0, gettid(), strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (read(fd, buf, len) != len) {
|
|
fprintf(stderr, "%s (%05d): ERROR: cannot read /dev/urandom: %s\n",
|
|
argv0, gettid(), strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static unsigned long long now(void)
|
|
{
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
return (tv.tv_sec * 1000ull) + (tv.tv_usec / 1000ull);
|
|
}
|
|
|
|
static void stressone(unsigned long long ramsizeMB)
|
|
{
|
|
size_t pagesPerMB = 1024 * 1024 / PAGE_SIZE;
|
|
g_autofree char *ram = g_malloc(ramsizeMB * 1024 * 1024);
|
|
char *ramptr;
|
|
size_t i, j, k;
|
|
g_autofree char *data = g_malloc(PAGE_SIZE);
|
|
char *dataptr;
|
|
size_t nMB = 0;
|
|
unsigned long long before, after;
|
|
|
|
/* We don't care about initial state, but we do want
|
|
* to fault it all into RAM, otherwise the first iter
|
|
* of the loop below will be quite slow. We can't use
|
|
* 0x0 as the byte as gcc optimizes that away into a
|
|
* calloc instead :-) */
|
|
memset(ram, 0xfe, ramsizeMB * 1024 * 1024);
|
|
|
|
if (random_bytes(data, PAGE_SIZE) < 0) {
|
|
return;
|
|
}
|
|
|
|
before = now();
|
|
|
|
while (1) {
|
|
|
|
ramptr = ram;
|
|
for (i = 0; i < ramsizeMB; i++, nMB++) {
|
|
for (j = 0; j < pagesPerMB; j++) {
|
|
dataptr = data;
|
|
for (k = 0; k < PAGE_SIZE; k += sizeof(long long)) {
|
|
ramptr += sizeof(long long);
|
|
dataptr += sizeof(long long);
|
|
*(unsigned long long *)ramptr ^= *(unsigned long long *)dataptr;
|
|
}
|
|
}
|
|
|
|
if (nMB == 1024) {
|
|
after = now();
|
|
fprintf(stderr, "%s (%05d): INFO: %06llums copied 1 GB in %05llums\n",
|
|
argv0, gettid(), after, after - before);
|
|
before = now();
|
|
nMB = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void *stressthread(void *arg)
|
|
{
|
|
unsigned long long ramsizeMB = *(unsigned long long *)arg;
|
|
|
|
stressone(ramsizeMB);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void stress(unsigned long long ramsizeGB, int ncpus)
|
|
{
|
|
size_t i;
|
|
unsigned long long ramsizeMB = ramsizeGB * 1024 / ncpus;
|
|
ncpus--;
|
|
|
|
for (i = 0; i < ncpus; i++) {
|
|
pthread_t thr;
|
|
pthread_create(&thr, NULL,
|
|
stressthread, &ramsizeMB);
|
|
}
|
|
|
|
stressone(ramsizeMB);
|
|
}
|
|
|
|
|
|
static int mount_misc(const char *fstype, const char *dir)
|
|
{
|
|
if (mkdir(dir, 0755) < 0 && errno != EEXIST) {
|
|
fprintf(stderr, "%s (%05d): ERROR: cannot create %s: %s\n",
|
|
argv0, gettid(), dir, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (mount("none", dir, fstype, 0, NULL) < 0) {
|
|
fprintf(stderr, "%s (%05d): ERROR: cannot mount %s: %s\n",
|
|
argv0, gettid(), dir, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mount_all(void)
|
|
{
|
|
if (mount_misc("proc", "/proc") < 0 ||
|
|
mount_misc("sysfs", "/sys") < 0 ||
|
|
mount_misc("tmpfs", "/dev") < 0)
|
|
return -1;
|
|
|
|
mknod("/dev/urandom", 0777 | S_IFCHR, makedev(1, 9));
|
|
mknod("/dev/random", 0777 | S_IFCHR, makedev(1, 8));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
unsigned long long ramsizeGB = 1;
|
|
char *end;
|
|
int ch;
|
|
int opt_ind = 0;
|
|
const char *sopt = "hr:c:";
|
|
struct option lopt[] = {
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "ramsize", required_argument, NULL, 'r' },
|
|
{ "cpus", required_argument, NULL, 'c' },
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
int ret;
|
|
int ncpus = 0;
|
|
|
|
argv0 = argv[0];
|
|
|
|
while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
|
|
switch (ch) {
|
|
case 'r':
|
|
errno = 0;
|
|
ramsizeGB = strtoll(optarg, &end, 10);
|
|
if (errno != 0 || *end) {
|
|
fprintf(stderr, "%s (%05d): ERROR: Cannot parse RAM size %s\n",
|
|
argv0, gettid(), optarg);
|
|
exit_failure();
|
|
}
|
|
break;
|
|
|
|
case 'c':
|
|
errno = 0;
|
|
ncpus = strtoll(optarg, &end, 10);
|
|
if (errno != 0 || *end) {
|
|
fprintf(stderr, "%s (%05d): ERROR: Cannot parse CPU count %s\n",
|
|
argv0, gettid(), optarg);
|
|
exit_failure();
|
|
}
|
|
break;
|
|
|
|
case '?':
|
|
case 'h':
|
|
fprintf(stderr, "%s: [--help][--ramsize GB][--cpus N]\n", argv0);
|
|
exit_failure();
|
|
}
|
|
}
|
|
|
|
if (getpid() == 1) {
|
|
if (mount_all() < 0)
|
|
exit_failure();
|
|
|
|
ret = get_command_arg_ull("ramsize", &ramsizeGB);
|
|
if (ret < 0)
|
|
exit_failure();
|
|
}
|
|
|
|
if (ncpus == 0)
|
|
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
|
|
|
|
fprintf(stdout, "%s (%05d): INFO: RAM %llu GiB across %d CPUs\n",
|
|
argv0, gettid(), ramsizeGB, ncpus);
|
|
|
|
stress(ramsizeGB, ncpus);
|
|
|
|
exit_failure();
|
|
}
|