summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/.gitignore2
-rw-r--r--tests/Makefile5
-rw-r--r--tests/ahci-test.c115
-rw-r--r--tests/crypto-tls-x509-helpers.c2
-rw-r--r--tests/libqos/libqos.c28
-rw-r--r--tests/libqos/libqos.h1
-rwxr-xr-xtests/qemu-iotests/0305
-rw-r--r--tests/qemu-iotests/1362
-rw-r--r--tests/vhost-user-bridge.c220
-rw-r--r--tests/vhost-user-test.c7
10 files changed, 316 insertions, 71 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
index e96f569903..1e55722b6a 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -9,6 +9,7 @@ check-qom-proplist
rcutorture
test-aio
test-bitops
+test-blockjob-txn
test-coroutine
test-crypto-cipher
test-crypto-hash
@@ -45,6 +46,7 @@ test-string-input-visitor
test-string-output-visitor
test-thread-pool
test-throttle
+test-timed-average
test-visitor-serialization
test-vmstate
test-write-threshold
diff --git a/tests/Makefile b/tests/Makefile
index 90c4141ac5..b9379841d8 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -197,8 +197,9 @@ gcov-files-i386-y += hw/usb/hcd-xhci.c
check-qtest-i386-y += tests/pc-cpu-test$(EXESUF)
check-qtest-i386-y += tests/q35-test$(EXESUF)
gcov-files-i386-y += hw/pci-host/q35.c
-ifeq ($(CONFIG_VHOST_NET),y)
-check-qtest-i386-$(CONFIG_LINUX) += tests/vhost-user-test$(EXESUF)
+check-qtest-i386-$(CONFIG_VHOST_NET_TEST_i386) += tests/vhost-user-test$(EXESUF)
+ifeq ($(CONFIG_VHOST_NET_TEST_i386),)
+check-qtest-x86_64-$(CONFIG_VHOST_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF)
endif
check-qtest-i386-y += tests/test-netfilter$(EXESUF)
check-qtest-x86_64-y = $(check-qtest-i386-y)
diff --git a/tests/ahci-test.c b/tests/ahci-test.c
index 59d387c6d0..088850642e 100644
--- a/tests/ahci-test.c
+++ b/tests/ahci-test.c
@@ -39,15 +39,17 @@
#include "hw/pci/pci_ids.h"
#include "hw/pci/pci_regs.h"
-/* Test-specific defines -- in MiB */
-#define TEST_IMAGE_SIZE_MB (200 * 1024)
-#define TEST_IMAGE_SECTORS ((TEST_IMAGE_SIZE_MB / AHCI_SECTOR_SIZE) \
- * 1024 * 1024)
+/* Test images sizes in MB */
+#define TEST_IMAGE_SIZE_MB_LARGE (200 * 1024)
+#define TEST_IMAGE_SIZE_MB_SMALL 64
/*** Globals ***/
static char tmp_path[] = "/tmp/qtest.XXXXXX";
static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX";
+static char mig_socket[] = "/tmp/qtest-migration.XXXXXX";
static bool ahci_pedantic;
+static const char *imgfmt;
+static unsigned test_image_size_mb;
/*** Function Declarations ***/
static void ahci_test_port_spec(AHCIQState *ahci, uint8_t port);
@@ -60,6 +62,11 @@ static void ahci_test_pmcap(AHCIQState *ahci, uint8_t offset);
/*** Utilities ***/
+static uint64_t mb_to_sectors(uint64_t image_size_mb)
+{
+ return (image_size_mb * 1024 * 1024) / AHCI_SECTOR_SIZE;
+}
+
static void string_bswap16(uint16_t *s, size_t bytes)
{
g_assert_cmphex((bytes & 1), ==, 0);
@@ -114,8 +121,11 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri)
{
QOSState *tmp = to->parent;
QPCIDevice *dev = to->dev;
+ char *uri_local = NULL;
+
if (uri == NULL) {
- uri = "tcp:127.0.0.1:1234";
+ uri_local = g_strdup_printf("%s%s", "unix:", mig_socket);
+ uri = uri_local;
}
/* context will be 'to' after completion. */
@@ -135,6 +145,7 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri)
from->dev = dev;
verify_state(to);
+ g_free(uri_local);
}
/*** Test Setup & Teardown ***/
@@ -170,11 +181,11 @@ static AHCIQState *ahci_boot(const char *cli, ...)
va_end(ap);
} else {
cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s"
- ",format=qcow2"
+ ",format=%s"
" -M q35 "
"-device ide-hd,drive=drive0 "
"-global ide-hd.ver=%s";
- s = ahci_boot(cli, tmp_path, "testdisk", "version");
+ s = ahci_boot(cli, tmp_path, "testdisk", imgfmt, "version");
}
return s;
@@ -900,7 +911,7 @@ static void ahci_test_max(AHCIQState *ahci)
uint64_t nsect;
uint8_t port;
uint8_t cmd;
- uint64_t config_sect = TEST_IMAGE_SECTORS - 1;
+ uint64_t config_sect = mb_to_sectors(test_image_size_mb) - 1;
if (config_sect > 0xFFFFFF) {
cmd = CMD_READ_MAX_EXT;
@@ -1073,12 +1084,12 @@ static void test_flush_retry(void)
prepare_blkdebug_script(debug_path, "flush_to_disk");
ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0,"
- "format=qcow2,cache=writeback,"
+ "format=%s,cache=writeback,"
"rerror=stop,werror=stop "
"-M q35 "
"-device ide-hd,drive=drive0 ",
debug_path,
- tmp_path);
+ tmp_path, imgfmt);
/* Issue Flush Command and wait for error */
port = ahci_port_select(ahci);
@@ -1105,18 +1116,19 @@ static void test_flush_retry(void)
static void test_migrate_sanity(void)
{
AHCIQState *src, *dst;
- const char *uri = "tcp:127.0.0.1:1234";
+ char *uri = g_strdup_printf("unix:%s", mig_socket);
src = ahci_boot("-m 1024 -M q35 "
- "-hda %s ", tmp_path);
+ "-drive if=ide,file=%s,format=%s ", tmp_path, imgfmt);
dst = ahci_boot("-m 1024 -M q35 "
- "-hda %s "
- "-incoming %s", tmp_path, uri);
+ "-drive if=ide,file=%s,format=%s "
+ "-incoming %s", tmp_path, imgfmt, uri);
ahci_migrate(src, dst, uri);
ahci_shutdown(src);
ahci_shutdown(dst);
+ g_free(uri);
}
/**
@@ -1129,13 +1141,14 @@ static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write)
size_t bufsize = 4096;
unsigned char *tx = g_malloc(bufsize);
unsigned char *rx = g_malloc0(bufsize);
- const char *uri = "tcp:127.0.0.1:1234";
+ char *uri = g_strdup_printf("unix:%s", mig_socket);
src = ahci_boot_and_enable("-m 1024 -M q35 "
- "-hda %s ", tmp_path);
+ "-drive if=ide,format=%s,file=%s ",
+ imgfmt, tmp_path);
dst = ahci_boot("-m 1024 -M q35 "
- "-hda %s "
- "-incoming %s", tmp_path, uri);
+ "-drive if=ide,format=%s,file=%s "
+ "-incoming %s", imgfmt, tmp_path, uri);
set_context(src->parent);
@@ -1158,6 +1171,7 @@ static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write)
ahci_shutdown(dst);
g_free(rx);
g_free(tx);
+ g_free(uri);
}
static void test_migrate_dma(void)
@@ -1190,12 +1204,12 @@ static void ahci_halted_io_test(uint8_t cmd_read, uint8_t cmd_write)
prepare_blkdebug_script(debug_path, "write_aio");
ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0,"
- "format=qcow2,cache=writeback,"
+ "format=%s,cache=writeback,"
"rerror=stop,werror=stop "
"-M q35 "
"-device ide-hd,drive=drive0 ",
debug_path,
- tmp_path);
+ tmp_path, imgfmt);
/* Initialize and prepare */
port = ahci_port_select(ahci);
@@ -1251,25 +1265,25 @@ static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write)
unsigned char *rx = g_malloc0(bufsize);
uint64_t ptr;
AHCICommand *cmd;
- const char *uri = "tcp:127.0.0.1:1234";
+ char *uri = g_strdup_printf("unix:%s", mig_socket);
prepare_blkdebug_script(debug_path, "write_aio");
src = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0,"
- "format=qcow2,cache=writeback,"
+ "format=%s,cache=writeback,"
"rerror=stop,werror=stop "
"-M q35 "
"-device ide-hd,drive=drive0 ",
debug_path,
- tmp_path);
+ tmp_path, imgfmt);
dst = ahci_boot("-drive file=%s,if=none,id=drive0,"
- "format=qcow2,cache=writeback,"
+ "format=%s,cache=writeback,"
"rerror=stop,werror=stop "
"-M q35 "
"-device ide-hd,drive=drive0 "
"-incoming %s",
- tmp_path, uri);
+ tmp_path, imgfmt, uri);
set_context(src->parent);
@@ -1301,6 +1315,7 @@ static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write)
ahci_shutdown(dst);
g_free(rx);
g_free(tx);
+ g_free(uri);
}
static void test_migrate_halted_dma(void)
@@ -1322,20 +1337,22 @@ static void test_flush_migrate(void)
AHCICommand *cmd;
uint8_t px;
const char *s;
- const char *uri = "tcp:127.0.0.1:1234";
+ char *uri = g_strdup_printf("unix:%s", mig_socket);
prepare_blkdebug_script(debug_path, "flush_to_disk");
src = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0,"
- "cache=writeback,rerror=stop,werror=stop "
+ "cache=writeback,rerror=stop,werror=stop,"
+ "format=%s "
"-M q35 "
"-device ide-hd,drive=drive0 ",
- debug_path, tmp_path);
+ debug_path, tmp_path, imgfmt);
dst = ahci_boot("-drive file=%s,if=none,id=drive0,"
- "cache=writeback,rerror=stop,werror=stop "
+ "cache=writeback,rerror=stop,werror=stop,"
+ "format=%s "
"-M q35 "
"-device ide-hd,drive=drive0 "
- "-incoming %s", tmp_path, uri);
+ "-incoming %s", tmp_path, imgfmt, uri);
set_context(src->parent);
@@ -1360,6 +1377,7 @@ static void test_flush_migrate(void)
ahci_command_free(cmd);
ahci_shutdown(src);
ahci_shutdown(dst);
+ g_free(uri);
}
static void test_max(void)
@@ -1476,7 +1494,7 @@ static uint64_t offset_sector(enum OffsetType ofst,
return 1;
case OFFSET_HIGH:
ceil = (addr_type == ADDR_MODE_LBA28) ? 0xfffffff : 0xffffffffffff;
- ceil = MIN(ceil, TEST_IMAGE_SECTORS - 1);
+ ceil = MIN(ceil, mb_to_sectors(test_image_size_mb) - 1);
nsectors = buffsize / AHCI_SECTOR_SIZE;
return ceil - nsectors + 1;
default:
@@ -1558,8 +1576,9 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr,
enum BuffLen len, enum OffsetType offset)
{
char *name;
- AHCIIOTestOptions *opts = g_malloc(sizeof(AHCIIOTestOptions));
+ AHCIIOTestOptions *opts;
+ opts = g_malloc(sizeof(AHCIIOTestOptions));
opts->length = len;
opts->address_type = addr;
opts->io_type = type;
@@ -1571,6 +1590,13 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr,
buff_len_str[len],
offset_str[offset]);
+ if ((addr == ADDR_MODE_LBA48) && (offset == OFFSET_HIGH) &&
+ (mb_to_sectors(test_image_size_mb) <= 0xFFFFFFF)) {
+ g_test_message("%s: skipped; test image too small", name);
+ g_free(name);
+ return;
+ }
+
qtest_add_data_func(name, opts, test_io_interface);
g_free(name);
}
@@ -1617,15 +1643,33 @@ int main(int argc, char **argv)
return 0;
}
- /* Create a temporary qcow2 image */
- close(mkstemp(tmp_path));
- mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB);
+ /* Create a temporary image */
+ fd = mkstemp(tmp_path);
+ g_assert(fd >= 0);
+ if (have_qemu_img()) {
+ imgfmt = "qcow2";
+ test_image_size_mb = TEST_IMAGE_SIZE_MB_LARGE;
+ mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB_LARGE);
+ } else {
+ g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; "
+ "skipping LBA48 high-sector tests");
+ imgfmt = "raw";
+ test_image_size_mb = TEST_IMAGE_SIZE_MB_SMALL;
+ ret = ftruncate(fd, test_image_size_mb * 1024 * 1024);
+ g_assert(ret == 0);
+ }
+ close(fd);
/* Create temporary blkdebug instructions */
fd = mkstemp(debug_path);
g_assert(fd >= 0);
close(fd);
+ /* Reserve a hollow file to use as a socket for migration tests */
+ fd = mkstemp(mig_socket);
+ g_assert(fd >= 0);
+ close(fd);
+
/* Run the tests */
qtest_add_func("/ahci/sanity", test_sanity);
qtest_add_func("/ahci/pci_spec", test_pci_spec);
@@ -1668,6 +1712,7 @@ int main(int argc, char **argv)
/* Cleanup */
unlink(tmp_path);
unlink(debug_path);
+ unlink(mig_socket);
return ret;
}
diff --git a/tests/crypto-tls-x509-helpers.c b/tests/crypto-tls-x509-helpers.c
index c5de67baaf..47b4c7ba53 100644
--- a/tests/crypto-tls-x509-helpers.c
+++ b/tests/crypto-tls-x509-helpers.c
@@ -153,6 +153,7 @@ test_tls_get_ipaddr(const char *addrstr,
*datalen = res->ai_addrlen;
*data = g_new(char, *datalen);
memcpy(*data, res->ai_addr, *datalen);
+ freeaddrinfo(res);
}
/*
@@ -465,6 +466,7 @@ void test_tls_write_cert_chain(const char *filename,
if (!g_file_set_contents(filename, buffer, offset, NULL)) {
abort();
}
+ g_free(buffer);
}
diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c
index 8d7c5a9db8..2d1a802dbe 100644
--- a/tests/libqos/libqos.c
+++ b/tests/libqos/libqos.c
@@ -147,6 +147,23 @@ void migrate(QOSState *from, QOSState *to, const char *uri)
set_context(to);
}
+bool have_qemu_img(void)
+{
+ char *rpath;
+ const char *path = getenv("QTEST_QEMU_IMG");
+ if (!path) {
+ return false;
+ }
+
+ rpath = realpath(path, NULL);
+ if (!rpath) {
+ return false;
+ } else {
+ free(rpath);
+ return true;
+ }
+}
+
void mkimg(const char *file, const char *fmt, unsigned size_mb)
{
gchar *cli;
@@ -155,13 +172,14 @@ void mkimg(const char *file, const char *fmt, unsigned size_mb)
GError *err = NULL;
char *qemu_img_path;
gchar *out, *out2;
- char *abs_path;
+ char *qemu_img_abs_path;
qemu_img_path = getenv("QTEST_QEMU_IMG");
- abs_path = realpath(qemu_img_path, NULL);
- assert(qemu_img_path);
+ g_assert(qemu_img_path);
+ qemu_img_abs_path = realpath(qemu_img_path, NULL);
+ g_assert(qemu_img_abs_path);
- cli = g_strdup_printf("%s create -f %s %s %uM", abs_path,
+ cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path,
fmt, file, size_mb);
ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err);
if (err) {
@@ -183,7 +201,7 @@ void mkimg(const char *file, const char *fmt, unsigned size_mb)
g_free(out);
g_free(out2);
g_free(cli);
- free(abs_path);
+ free(qemu_img_abs_path);
}
void mkqcow2(const char *file, unsigned size_mb)
diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h
index 492a651f5b..ca14d2e9fe 100644
--- a/tests/libqos/libqos.h
+++ b/tests/libqos/libqos.h
@@ -19,6 +19,7 @@ typedef struct QOSState {
QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap);
QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...);
void qtest_shutdown(QOSState *qs);
+bool have_qemu_img(void);
void mkimg(const char *file, const char *fmt, unsigned size_mb);
void mkqcow2(const char *file, unsigned size_mb);
void set_context(QOSState *s);
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
index 952a524ec7..32469efd76 100755
--- a/tests/qemu-iotests/030
+++ b/tests/qemu-iotests/030
@@ -245,6 +245,7 @@ class TestEIO(TestErrors):
while not completed:
for event in self.vm.get_qmp_events(wait=True):
if event['event'] == 'BLOCK_JOB_ERROR':
+ error = True
self.assert_qmp(event, 'data/device', 'drive0')
self.assert_qmp(event, 'data/operation', 'read')
@@ -257,9 +258,11 @@ class TestEIO(TestErrors):
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('query-block-jobs')
+ if result == {'return': []}:
+ # Race; likely already finished. Check.
+ continue
self.assert_qmp(result, 'return[0]/paused', False)
self.assert_qmp(result, 'return[0]/io-status', 'ok')
- error = True
elif event['event'] == 'BLOCK_JOB_COMPLETED':
self.assertTrue(error, 'job completed unexpectedly')
self.assert_qmp(event, 'data/type', 'stream')
diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136
index f574d83ff7..e8c6937fc9 100644
--- a/tests/qemu-iotests/136
+++ b/tests/qemu-iotests/136
@@ -69,7 +69,7 @@ sector = "%d"
def setUp(self):
drive_args = []
- drive_args.append("stats-intervals=%d" % interval_length)
+ drive_args.append("stats-intervals.0=%d" % interval_length)
drive_args.append("stats-account-invalid=%s" %
(self.account_invalid and "on" or "off"))
drive_args.append("stats-account-failed=%s" %
diff --git a/tests/vhost-user-bridge.c b/tests/vhost-user-bridge.c
index 864f69e738..7bdfc98615 100644
--- a/tests/vhost-user-bridge.c
+++ b/tests/vhost-user-bridge.c
@@ -13,16 +13,22 @@
/*
* TODO:
* - main should get parameters from the command line.
- * - implement all request handlers.
+ * - implement all request handlers. Still not implemented:
+ * vubr_get_queue_num_exec()
+ * vubr_send_rarp_exec()
* - test for broken requests and virtqueue.
* - implement features defined by Virtio 1.0 spec.
* - support mergeable buffers and indirect descriptors.
- * - implement RESET_DEVICE request.
* - implement clean shutdown.
* - implement non-blocking writes to UDP backend.
* - implement polling strategy.
+ * - implement clean starting/stopping of vq processing
+ * - implement clean starting/stopping of used and buffers
+ * dirty page logging.
*/
+#define _FILE_OFFSET_BITS 64
+
#include <stddef.h>
#include <assert.h>
#include <stdio.h>
@@ -166,6 +172,8 @@ typedef struct VubrVirtq {
struct vring_desc *desc;
struct vring_avail *avail;
struct vring_used *used;
+ uint64_t log_guest_addr;
+ int enable;
} VubrVirtq;
/* Based on qemu/hw/virtio/vhost-user.c */
@@ -173,6 +181,8 @@ typedef struct VubrVirtq {
#define VHOST_MEMORY_MAX_NREGIONS 8
#define VHOST_USER_F_PROTOCOL_FEATURES 30
+#define VHOST_LOG_PAGE 4096
+
enum VhostUserProtocolFeature {
VHOST_USER_PROTOCOL_F_MQ = 0,
VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
@@ -220,6 +230,11 @@ typedef struct VhostUserMemory {
VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
} VhostUserMemory;
+typedef struct VhostUserLog {
+ uint64_t mmap_size;
+ uint64_t mmap_offset;
+} VhostUserLog;
+
typedef struct VhostUserMsg {
VhostUserRequest request;
@@ -234,6 +249,7 @@ typedef struct VhostUserMsg {
struct vhost_vring_state state;
struct vhost_vring_addr addr;
VhostUserMemory memory;
+ VhostUserLog log;
} payload;
int fds[VHOST_MEMORY_MAX_NREGIONS];
int fd_num;
@@ -265,8 +281,13 @@ typedef struct VubrDev {
uint32_t nregions;
VubrDevRegion regions[VHOST_MEMORY_MAX_NREGIONS];
VubrVirtq vq[MAX_NR_VIRTQUEUE];
+ int log_call_fd;
+ uint64_t log_size;
+ uint8_t *log_table;
int backend_udp_sock;
struct sockaddr_in backend_udp_dest;
+ int ready;
+ uint64_t features;
} VubrDev;
static const char *vubr_request_str[] = {
@@ -368,7 +389,12 @@ vubr_message_read(int conn_fd, VhostUserMsg *vmsg)
rc = recvmsg(conn_fd, &msg, 0);
- if (rc <= 0) {
+ if (rc == 0) {
+ vubr_die("recvmsg");
+ fprintf(stderr, "Peer disconnected.\n");
+ exit(1);
+ }
+ if (rc < 0) {
vubr_die("recvmsg");
}
@@ -395,7 +421,12 @@ vubr_message_read(int conn_fd, VhostUserMsg *vmsg)
if (vmsg->size) {
rc = read(conn_fd, &vmsg->payload, vmsg->size);
- if (rc <= 0) {
+ if (rc == 0) {
+ vubr_die("recvmsg");
+ fprintf(stderr, "Peer disconnected.\n");
+ exit(1);
+ }
+ if (rc < 0) {
vubr_die("recvmsg");
}
@@ -455,6 +486,16 @@ vubr_consume_raw_packet(VubrDev *dev, uint8_t *buf, uint32_t len)
vubr_backend_udp_sendbuf(dev, buf + hdrlen, len - hdrlen);
}
+/* Kick the log_call_fd if required. */
+static void
+vubr_log_kick(VubrDev *dev)
+{
+ if (dev->log_call_fd != -1) {
+ DPRINT("Kicking the QEMU's log...\n");
+ eventfd_write(dev->log_call_fd, 1);
+ }
+}
+
/* Kick the guest if necessary. */
static void
vubr_virtqueue_kick(VubrVirtq *vq)
@@ -466,11 +507,39 @@ vubr_virtqueue_kick(VubrVirtq *vq)
}
static void
+vubr_log_page(uint8_t *log_table, uint64_t page)
+{
+ DPRINT("Logged dirty guest page: %"PRId64"\n", page);
+ atomic_or(&log_table[page / 8], 1 << (page % 8));
+}
+
+static void
+vubr_log_write(VubrDev *dev, uint64_t address, uint64_t length)
+{
+ uint64_t page;
+
+ if (!(dev->features & (1ULL << VHOST_F_LOG_ALL)) ||
+ !dev->log_table || !length) {
+ return;
+ }
+
+ assert(dev->log_size > ((address + length - 1) / VHOST_LOG_PAGE / 8));
+
+ page = address / VHOST_LOG_PAGE;
+ while (page * VHOST_LOG_PAGE < address + length) {
+ vubr_log_page(dev->log_table, page);
+ page += VHOST_LOG_PAGE;
+ }
+ vubr_log_kick(dev);
+}
+
+static void
vubr_post_buffer(VubrDev *dev, VubrVirtq *vq, uint8_t *buf, int32_t len)
{
- struct vring_desc *desc = vq->desc;
+ struct vring_desc *desc = vq->desc;
struct vring_avail *avail = vq->avail;
- struct vring_used *used = vq->used;
+ struct vring_used *used = vq->used;
+ uint64_t log_guest_addr = vq->log_guest_addr;
unsigned int size = vq->size;
@@ -510,6 +579,7 @@ vubr_post_buffer(VubrDev *dev, VubrVirtq *vq, uint8_t *buf, int32_t len)
if (len <= chunk_len) {
memcpy(chunk_start, buf, len);
+ vubr_log_write(dev, desc[i].addr, len);
} else {
fprintf(stderr,
"Received too long packet from the backend. Dropping...\n");
@@ -519,11 +589,17 @@ vubr_post_buffer(VubrDev *dev, VubrVirtq *vq, uint8_t *buf, int32_t len)
/* Add descriptor to the used ring. */
used->ring[u_index].id = d_index;
used->ring[u_index].len = len;
+ vubr_log_write(dev,
+ log_guest_addr + offsetof(struct vring_used, ring[u_index]),
+ sizeof(used->ring[u_index]));
vq->last_avail_index++;
vq->last_used_index++;
atomic_mb_set(&used->idx, vq->last_used_index);
+ vubr_log_write(dev,
+ log_guest_addr + offsetof(struct vring_used, idx),
+ sizeof(used->idx));
/* Kick the guest if necessary. */
vubr_virtqueue_kick(vq);
@@ -532,9 +608,10 @@ vubr_post_buffer(VubrDev *dev, VubrVirtq *vq, uint8_t *buf, int32_t len)
static int
vubr_process_desc(VubrDev *dev, VubrVirtq *vq)
{
- struct vring_desc *desc = vq->desc;
+ struct vring_desc *desc = vq->desc;
struct vring_avail *avail = vq->avail;
- struct vring_used *used = vq->used;
+ struct vring_used *used = vq->used;
+ uint64_t log_guest_addr = vq->log_guest_addr;
unsigned int size = vq->size;
@@ -552,6 +629,8 @@ vubr_process_desc(VubrDev *dev, VubrVirtq *vq)
void *chunk_start = (void *)gpa_to_va(dev, desc[i].addr);
uint32_t chunk_len = desc[i].len;
+ assert(!(desc[i].flags & VRING_DESC_F_WRITE));
+
if (len + chunk_len < buf_size) {
memcpy(buf + len, chunk_start, chunk_len);
DPRINT("%d ", chunk_len);
@@ -577,6 +656,9 @@ vubr_process_desc(VubrDev *dev, VubrVirtq *vq)
/* Add descriptor to the used ring. */
used->ring[u_index].id = d_index;
used->ring[u_index].len = len;
+ vubr_log_write(dev,
+ log_guest_addr + offsetof(struct vring_used, ring[u_index]),
+ sizeof(used->ring[u_index]));
vubr_consume_raw_packet(dev, buf, len);
@@ -588,6 +670,7 @@ vubr_process_avail(VubrDev *dev, VubrVirtq *vq)
{
struct vring_avail *avail = vq->avail;
struct vring_used *used = vq->used;
+ uint64_t log_guest_addr = vq->log_guest_addr;
while (vq->last_avail_index != atomic_mb_read(&avail->idx)) {
vubr_process_desc(dev, vq);
@@ -596,6 +679,9 @@ vubr_process_avail(VubrDev *dev, VubrVirtq *vq)
}
atomic_mb_set(&used->idx, vq->last_used_index);
+ vubr_log_write(dev,
+ log_guest_addr + offsetof(struct vring_used, idx),
+ sizeof(used->idx));
}
static void
@@ -609,6 +695,10 @@ vubr_backend_recv_cb(int sock, void *ctx)
int buflen = sizeof(buf);
int len;
+ if (!dev->ready) {
+ return;
+ }
+
DPRINT("\n\n *** IN UDP RECEIVE CALLBACK ***\n\n");
uint16_t avail_index = atomic_mb_read(&rx_vq->avail->idx);
@@ -656,14 +746,14 @@ vubr_get_features_exec(VubrDev *dev, VhostUserMsg *vmsg)
{
vmsg->payload.u64 =
((1ULL << VIRTIO_NET_F_MRG_RXBUF) |
- (1ULL << VIRTIO_NET_F_CTRL_VQ) |
- (1ULL << VIRTIO_NET_F_CTRL_RX) |
- (1ULL << VHOST_F_LOG_ALL));
+ (1ULL << VHOST_F_LOG_ALL) |
+ (1ULL << VHOST_USER_F_PROTOCOL_FEATURES));
+
vmsg->size = sizeof(vmsg->payload.u64);
DPRINT("Sending back to guest u64: 0x%016"PRIx64"\n", vmsg->payload.u64);
- /* reply */
+ /* Reply */
return 1;
}
@@ -671,6 +761,7 @@ static int
vubr_set_features_exec(VubrDev *dev, VhostUserMsg *vmsg)
{
DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64);
+ dev->features = vmsg->payload.u64;
return 0;
}
@@ -680,10 +771,28 @@ vubr_set_owner_exec(VubrDev *dev, VhostUserMsg *vmsg)
return 0;
}
+static void
+vubr_close_log(VubrDev *dev)
+{
+ if (dev->log_table) {
+ if (munmap(dev->log_table, dev->log_size) != 0) {
+ vubr_die("munmap()");
+ }
+
+ dev->log_table = 0;
+ }
+ if (dev->log_call_fd != -1) {
+ close(dev->log_call_fd);
+ dev->log_call_fd = -1;
+ }
+}
+
static int
vubr_reset_device_exec(VubrDev *dev, VhostUserMsg *vmsg)
{
- DPRINT("Function %s() not implemented yet.\n", __func__);
+ vubr_close_log(dev);
+ dev->ready = 0;
+ dev->features = 0;
return 0;
}
@@ -710,9 +819,9 @@ vubr_set_mem_table_exec(VubrDev *dev, VhostUserMsg *vmsg)
DPRINT(" mmap_offset 0x%016"PRIx64"\n",
msg_region->mmap_offset);
- dev_region->gpa = msg_region->guest_phys_addr;
- dev_region->size = msg_region->memory_size;
- dev_region->qva = msg_region->userspace_addr;
+ dev_region->gpa = msg_region->guest_phys_addr;
+ dev_region->size = msg_region->memory_size;
+ dev_region->qva = msg_region->userspace_addr;
dev_region->mmap_offset = msg_region->mmap_offset;
/* We don't use offset argument of mmap() since the
@@ -736,14 +845,38 @@ vubr_set_mem_table_exec(VubrDev *dev, VhostUserMsg *vmsg)
static int
vubr_set_log_base_exec(VubrDev *dev, VhostUserMsg *vmsg)
{
- DPRINT("Function %s() not implemented yet.\n", __func__);
- return 0;
+ int fd;
+ uint64_t log_mmap_size, log_mmap_offset;
+ void *rc;
+
+ assert(vmsg->fd_num == 1);
+ fd = vmsg->fds[0];
+
+ assert(vmsg->size == sizeof(vmsg->payload.log));
+ log_mmap_offset = vmsg->payload.log.mmap_offset;
+ log_mmap_size = vmsg->payload.log.mmap_size;
+ DPRINT("Log mmap_offset: %"PRId64"\n", log_mmap_offset);
+ DPRINT("Log mmap_size: %"PRId64"\n", log_mmap_size);
+
+ rc = mmap(0, log_mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ log_mmap_offset);
+ if (rc == MAP_FAILED) {
+ vubr_die("mmap");
+ }
+ dev->log_table = rc;
+ dev->log_size = log_mmap_size;
+
+ vmsg->size = sizeof(vmsg->payload.u64);
+ /* Reply */
+ return 1;
}
static int
vubr_set_log_fd_exec(VubrDev *dev, VhostUserMsg *vmsg)
{
- DPRINT("Function %s() not implemented yet.\n", __func__);
+ assert(vmsg->fd_num == 1);
+ dev->log_call_fd = vmsg->fds[0];
+ DPRINT("Got log_call_fd: %d\n", vmsg->fds[0]);
return 0;
}
@@ -777,6 +910,7 @@ vubr_set_vring_addr_exec(VubrDev *dev, VhostUserMsg *vmsg)
vq->desc = (struct vring_desc *)qva_to_va(dev, vra->desc_user_addr);
vq->used = (struct vring_used *)qva_to_va(dev, vra->used_user_addr);
vq->avail = (struct vring_avail *)qva_to_va(dev, vra->avail_user_addr);
+ vq->log_guest_addr = vra->log_guest_addr;
DPRINT("Setting virtq addresses:\n");
DPRINT(" vring_desc at %p\n", vq->desc);
@@ -803,8 +937,18 @@ vubr_set_vring_base_exec(VubrDev *dev, VhostUserMsg *vmsg)
static int
vubr_get_vring_base_exec(VubrDev *dev, VhostUserMsg *vmsg)
{
- DPRINT("Function %s() not implemented yet.\n", __func__);
- return 0;
+ unsigned int index = vmsg->payload.state.index;
+
+ DPRINT("State.index: %d\n", index);
+ vmsg->payload.state.num = dev->vq[index].last_avail_index;
+ vmsg->size = sizeof(vmsg->payload.state);
+ /* FIXME: this is a work-around for a bug in QEMU enabling
+ * too early vrings. When protocol features are enabled,
+ * we have to respect * VHOST_USER_SET_VRING_ENABLE request. */
+ dev->ready = 0;
+
+ /* Reply */
+ return 1;
}
static int
@@ -829,7 +973,17 @@ vubr_set_vring_kick_exec(VubrDev *dev, VhostUserMsg *vmsg)
DPRINT("Waiting for kicks on fd: %d for vq: %d\n",
dev->vq[index].kick_fd, index);
}
+ /* We temporarily use this hack to determine that both TX and RX
+ * queues are set up and ready for processing.
+ * FIXME: we need to rely in VHOST_USER_SET_VRING_ENABLE and
+ * actual kicks. */
+ if (dev->vq[0].kick_fd != -1 &&
+ dev->vq[1].kick_fd != -1) {
+ dev->ready = 1;
+ DPRINT("vhost-user-bridge is ready for processing queues.\n");
+ }
return 0;
+
}
static int
@@ -858,9 +1012,12 @@ vubr_set_vring_err_exec(VubrDev *dev, VhostUserMsg *vmsg)
static int
vubr_get_protocol_features_exec(VubrDev *dev, VhostUserMsg *vmsg)
{
- /* FIXME: unimplented */
+ vmsg->payload.u64 = 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD;
DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64);
- return 0;
+ vmsg->size = sizeof(vmsg->payload.u64);
+
+ /* Reply */
+ return 1;
}
static int
@@ -881,7 +1038,12 @@ vubr_get_queue_num_exec(VubrDev *dev, VhostUserMsg *vmsg)
static int
vubr_set_vring_enable_exec(VubrDev *dev, VhostUserMsg *vmsg)
{
- DPRINT("Function %s() not implemented yet.\n", __func__);
+ unsigned int index = vmsg->payload.state.index;
+ unsigned int enable = vmsg->payload.state.num;
+
+ DPRINT("State.index: %d\n", index);
+ DPRINT("State.enable: %d\n", enable);
+ dev->vq[index].enable = enable;
return 0;
}
@@ -987,7 +1149,7 @@ vubr_accept_cb(int sock, void *ctx)
socklen_t len = sizeof(un);
conn_fd = accept(sock, (struct sockaddr *) &un, &len);
- if (conn_fd == -1) {
+ if (conn_fd == -1) {
vubr_die("accept()");
}
DPRINT("Got connection from remote peer on sock %d\n", conn_fd);
@@ -1009,9 +1171,17 @@ vubr_new(const char *path)
.size = 0,
.last_avail_index = 0, .last_used_index = 0,
.desc = 0, .avail = 0, .used = 0,
+ .enable = 0,
};
}
+ /* Init log */
+ dev->log_call_fd = -1;
+ dev->log_size = 0;
+ dev->log_table = 0;
+ dev->ready = 0;
+ dev->features = 0;
+
/* Get a UNIX socket. */
dev->sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (dev->sock == -1) {
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index 01cfc7e25d..022223b2a7 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -70,6 +70,7 @@ typedef enum VhostUserRequest {
VHOST_USER_SET_VRING_ERR = 14,
VHOST_USER_GET_PROTOCOL_FEATURES = 15,
VHOST_USER_SET_PROTOCOL_FEATURES = 16,
+ VHOST_USER_SET_VRING_ENABLE = 18,
VHOST_USER_MAX
} VhostUserRequest;
@@ -315,8 +316,10 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
g_cond_signal(&s->data_cond);
break;
- case VHOST_USER_RESET_OWNER:
- s->fds_num = 0;
+ case VHOST_USER_SET_VRING_ENABLE:
+ if (!msg.payload.state.num) {
+ s->fds_num = 0;
+ }
break;
default: