// SPDX-License-Identifier: GPL-2.0-only /* * vsock test utilities * * Copyright (C) 2017 Red Hat, Inc. * * Author: Stefan Hajnoczi <stefanha@redhat.com>
*/
/* Wait until ioctl gives an expected int value. * Return false if the op is not supported.
*/ bool vsock_ioctl_int(int fd, unsignedlong op, int expected)
{ int actual, ret; char name[32];
snprintf(name, sizeof(name), "ioctl(%lu)", op);
timeout_begin(TIMEOUT); do {
ret = ioctl(fd, op, &actual); if (ret < 0) { if (errno == EOPNOTSUPP || errno == ENOTTY) break;
perror(name); exit(EXIT_FAILURE);
}
timeout_check(name);
} while (actual != expected);
timeout_end();
return ret >= 0;
}
/* Wait until transport reports no data left to be sent. * Return false if transport does not implement the unsent_bytes() callback.
*/ bool vsock_wait_sent(int fd)
{ return vsock_ioctl_int(fd, SIOCOUTQ, 0);
}
/* Create socket <type>, bind to <cid, port>. * Return the file descriptor, or -1 on error.
*/ int vsock_bind_try(unsignedint cid, unsignedint port, int type)
{ struct sockaddr_vm sa = {
.svm_family = AF_VSOCK,
.svm_cid = cid,
.svm_port = port,
}; int fd, saved_errno;
int vsock_connect_fd(int fd, unsignedint cid, unsignedint port)
{ struct sockaddr_vm sa = {
.svm_family = AF_VSOCK,
.svm_cid = cid,
.svm_port = port,
}; int ret;
timeout_begin(TIMEOUT); do {
ret = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
timeout_check("connect");
} while (ret < 0 && errno == EINTR);
timeout_end();
return ret;
}
/* Bind to <bind_port>, connect to <cid, port> and return the file descriptor. */ int vsock_bind_connect(unsignedint cid, unsignedint port, unsignedint bind_port, int type)
{ int client_fd;
/* Listen on <cid, port> and return the file descriptor. */ staticint vsock_listen(unsignedint cid, unsignedint port, int type)
{ int fd;
fd = vsock_bind(cid, port, type);
if (listen(fd, 1) < 0) {
perror("listen"); exit(EXIT_FAILURE);
}
return fd;
}
/* Listen on <cid, port> and return the first incoming connection. The remote * address is stored to clientaddrp. clientaddrp may be NULL.
*/ int vsock_accept(unsignedint cid, unsignedint port, struct sockaddr_vm *clientaddrp, int type)
{ union { struct sockaddr sa; struct sockaddr_vm svm;
} clientaddr;
socklen_t clientaddr_len = sizeof(clientaddr.svm); int fd, client_fd, old_errno;
fd = vsock_listen(cid, port, type);
control_writeln("LISTENING");
timeout_begin(TIMEOUT); do {
client_fd = accept(fd, &clientaddr.sa, &clientaddr_len);
timeout_check("accept");
} while (client_fd < 0 && errno == EINTR);
timeout_end();
old_errno = errno;
close(fd);
errno = old_errno;
if (client_fd < 0) return client_fd;
if (clientaddr_len != sizeof(clientaddr.svm)) {
fprintf(stderr, "unexpected addrlen from accept(2), %zu\n",
(size_t)clientaddr_len); exit(EXIT_FAILURE);
} if (clientaddr.sa.sa_family != AF_VSOCK) {
fprintf(stderr, "expected AF_VSOCK from accept(2), got %d\n",
clientaddr.sa.sa_family); exit(EXIT_FAILURE);
}
if (clientaddrp)
*clientaddrp = clientaddr.svm; return client_fd;
}
/* Run test cases. The program terminates if a failure occurs. */ void run_tests(conststruct test_case *test_cases, conststruct test_opts *opts)
{ int i;
for (i = 0; test_cases[i].name; i++) { void (*run)(conststruct test_opts *opts); char *line;
printf("%d - %s...", i, test_cases[i].name);
fflush(stdout);
/* Full barrier before executing the next test. This * ensures that client and server are executing the * same test case. In particular, it means whoever is * faster will not see the peer still executing the * last test. This is important because port numbers * can be used by multiple test cases.
*/ if (test_cases[i].skip)
control_writeln("SKIP"); else
control_writeln("NEXT");
line = control_readln(); if (control_cmpln(line, "SKIP", false) || test_cases[i].skip) {
printf("skipped\n");
free(line); continue;
}
control_cmpln(line, "NEXT", true);
free(line);
if (opts->mode == TEST_MODE_CLIENT)
run = test_cases[i].run_client; else
run = test_cases[i].run_server;
if (run)
run(opts);
printf("ok\n");
}
}
void list_tests(conststruct test_case *test_cases)
{ int i;
printf("ID\tTest name\n");
for (i = 0; test_cases[i].name; i++)
printf("%d\t%s\n", i, test_cases[i].name);
errno = 0;
test_id = strtoul(test_id_str, &endptr, 10); if (errno || *endptr != '\0') {
fprintf(stderr, "malformed test ID \"%s\"\n", test_id_str); exit(EXIT_FAILURE);
}
if (test_id >= test_cases_len) {
fprintf(stderr, "test ID (%lu) larger than the max allowed (%lu)\n",
test_id, test_cases_len - 1); exit(EXIT_FAILURE);
}
tmp = malloc(iov_bytes); if (!tmp) {
perror("malloc"); exit(EXIT_FAILURE);
}
for (offs = 0, i = 0; i < iovnum; i++) {
memcpy(tmp + offs, iov[i].iov_base, iov[i].iov_len);
offs += iov[i].iov_len;
}
hash = hash_djb2(tmp, iov_bytes);
free(tmp);
return hash;
}
/* Allocates and returns new 'struct iovec *' according pattern * in the 'test_iovec'. For each element in the 'test_iovec' it * allocates new element in the resulting 'iovec'. 'iov_len' * of the new element is copied from 'test_iovec'. 'iov_base' is * allocated depending on the 'iov_base' of 'test_iovec': * * 'iov_base' == NULL -> valid buf: mmap('iov_len'). * * 'iov_base' == MAP_FAILED -> invalid buf: * mmap('iov_len'), then munmap('iov_len'). * 'iov_base' still contains result of * mmap(). * * 'iov_base' == number -> unaligned valid buf: * mmap('iov_len') + number. * * 'iovnum' is number of elements in 'test_iovec'. * * Returns new 'iovec' or calls 'exit()' on error.
*/ struct iovec *alloc_test_iovec(conststruct iovec *test_iovec, int iovnum)
{ struct iovec *iovec; int i;
/* Frees 'iovec *', previously allocated by 'alloc_test_iovec()'. * On error calls 'exit()'.
*/ void free_test_iovec(conststruct iovec *test_iovec, struct iovec *iovec, int iovnum)
{ int i;
for (i = 0; i < iovnum; i++) { if (test_iovec[i].iov_base != MAP_FAILED) { if (test_iovec[i].iov_base)
iovec[i].iov_base -= (uintptr_t)test_iovec[i].iov_base;
if (munmap(iovec[i].iov_base, iovec[i].iov_len)) {
perror("munmap"); exit(EXIT_FAILURE);
}
}
}
free(iovec);
}
/* Set "unsigned long long" socket option and check that it's indeed set */ void setsockopt_ull_check(int fd, int level, int optname, unsignedlonglong val, charconst *errmsg)
{ unsignedlonglong chkval;
socklen_t chklen; int err;
if (chklen != sizeof(chkval)) {
fprintf(stderr, "size mismatch: set %zu got %d\n", sizeof(val),
chklen); goto fail;
}
if (chkval != val) {
fprintf(stderr, "value mismatch: set %llu got %llu\n", val,
chkval); goto fail;
} return;
fail:
fprintf(stderr, "%s val %llu\n", errmsg, val); exit(EXIT_FAILURE);
;
}
/* Set "int" socket option and check that it's indeed set */ void setsockopt_int_check(int fd, int level, int optname, int val, charconst *errmsg)
{ int chkval;
socklen_t chklen; int err;
/* Set "timeval" socket option and check that it's indeed set */ void setsockopt_timeval_check(int fd, int level, int optname, struct timeval val, charconst *errmsg)
{ struct timeval chkval;
socklen_t chklen; int err;
staticint __get_transports(void)
{ char buf[KALLSYMS_LINE_LEN]; constchar *ksym; int ret = 0;
FILE *f;
f = fopen(KALLSYMS_PATH, "r"); if (!f) {
perror("Can't open " KALLSYMS_PATH); exit(EXIT_FAILURE);
}
while (fgets(buf, sizeof(buf), f)) { char *match; int i;
assert(buf[strlen(buf) - 1] == '\n');
for (i = 0; i < TRANSPORT_NUM; ++i) { if (ret & BIT(i)) continue;
/* Match should be followed by '\t' or '\n'. * See kallsyms.c:s_show().
*/
ksym = transport_ksyms[i];
match = strstr(buf, ksym); if (match && isspace(match[strlen(ksym)])) {
ret |= BIT(i); break;
}
}
}
fclose(f); return ret;
}
/* Return integer with TRANSPORT_* bit set for every (known) registered vsock * transport.
*/ int get_transports(void)
{ staticint tr = -1;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.