aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorThanos Makatos <thanos.makatos@nutanix.com>2021-02-10 09:08:10 +0000
committerGitHub <noreply@github.com>2021-02-10 09:08:10 +0000
commit365ca96a97740332d3633090d850222d10bc9d70 (patch)
treebd9885cd57351c05ac12d63ef51a4e4e5b3eef7d /test
parentc5d11659c95c995acb77a71fe03c38b240ca43d9 (diff)
downloadlibvfio-user-365ca96a97740332d3633090d850222d10bc9d70.zip
libvfio-user-365ca96a97740332d3633090d850222d10bc9d70.tar.gz
libvfio-user-365ca96a97740332d3633090d850222d10bc9d70.tar.bz2
expose migration region (#305)
This patch exposes the fact that live migration is implemented as a special device region. Hiding this from the user doesn't offer much benefit since it only takes just a little bit of extra code for the user to handle it as a region. We do keep the migration callback functionality since this feature substantially simplifies supporting live migration from the device implementation's perspective. Signed-off-by: Thanos Makatos <thanos.makatos@nutanix.com> Co-authored-by: John Levon <john.levon@nutanix.com>
Diffstat (limited to 'test')
-rw-r--r--test/CMakeLists.txt2
-rw-r--r--test/mocks.c23
-rw-r--r--test/unit-tests.c176
3 files changed, 198 insertions, 3 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 2a339f0..a4f49ad 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -53,6 +53,8 @@ target_link_libraries(unit-tests PUBLIC "-Wl,--wrap=close")
target_link_libraries(unit-tests PUBLIC "-Wl,--wrap=tran_sock_send_iovec")
target_link_libraries(unit-tests PUBLIC "-Wl,--wrap=free")
target_link_libraries(unit-tests PUBLIC "-Wl,--wrap=process_request")
+target_link_libraries(unit-tests PUBLIC "-Wl,--wrap=bind")
+target_link_libraries(unit-tests PUBLIC "-Wl,--wrap=listen")
enable_testing()
add_test(NAME unit-tests COMMAND unit-tests)
diff --git a/test/mocks.c b/test/mocks.c
index c38ec7b..303a6c2 100644
--- a/test/mocks.c
+++ b/test/mocks.c
@@ -33,6 +33,8 @@
#include <setjmp.h>
#include <cmocka.h>
#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
#include "mocks.h"
#include "dma.h"
@@ -124,6 +126,10 @@ __wrap_exec_command(vfu_ctx_t *vfu_ctx, struct vfio_user_header *hdr,
int
__wrap_close(int fd)
{
+ if (!is_patched(close)) {
+ return __real_close(fd);
+ }
+
check_expected(fd);
return mock();
}
@@ -168,6 +174,19 @@ __wrap_process_request(vfu_ctx_t *vfu_ctx)
return mock();
}
+int __wrap_bind(int sockfd __attribute__((unused)),
+ const struct sockaddr *addr __attribute__((unused)),
+ socklen_t addrlen __attribute__((unused)))
+{
+ return 0;
+}
+
+int __wrap_listen(int sockfd __attribute__((unused)),
+ int backlog __attribute__((unused)))
+{
+ return 0;
+}
+
/* FIXME should be something faster than unsorted array, look at tsearch(3). */
static struct function funcs[] = {
{.addr = &__wrap_dma_controller_add_region},
@@ -179,7 +198,9 @@ static struct function funcs[] = {
{.addr = &__wrap_close},
{.addr = &__wrap_tran_sock_send_iovec},
{.addr = &__wrap_free},
- {.addr = &__wrap_process_request}
+ {.addr = &__wrap_process_request},
+ {.addr = &__wrap_bind},
+ {.addr = &__wrap_listen}
};
static struct function*
diff --git a/test/unit-tests.c b/test/unit-tests.c
index c989d91..e3ad86e 100644
--- a/test/unit-tests.c
+++ b/test/unit-tests.c
@@ -39,6 +39,7 @@
#include <alloca.h>
#include <string.h>
#include <linux/pci_regs.h>
+#include <sys/param.h>
#include "dma.h"
#include "libvfio-user.h"
@@ -1202,11 +1203,158 @@ test_migration_state_transitions(void **state __attribute__ ((unused)))
* and provide a function to execute before and after each unit test.
*/
static int
-setup(void **state __attribute__((unused))) {
+setup(void **state __attribute__((unused)))
+{
unpatch_all();
return 0;
}
+static struct test_setup_migr_reg_dat {
+ vfu_ctx_t *v;
+ size_t rs; /* migration registers size */
+ size_t ds; /* migration data size */
+ size_t s; /* migration region size*/
+ const vfu_migration_callbacks_t c;
+} migr_reg_data = {
+ .c = {
+ .version = VFU_MIGR_CALLBACKS_VERS,
+ .transition = (void *)0x1,
+ .get_pending_bytes = (void *)0x2,
+ .prepare_data = (void *)0x3,
+ .read_data = (void *)0x4,
+ .write_data = (void *)0x5,
+ .data_written = (void *)0x6
+ }
+};
+
+static int
+setup_test_setup_migration_region(void **state)
+{
+ struct test_setup_migr_reg_dat *p = &migr_reg_data;
+ p->v = vfu_create_ctx(VFU_TRANS_SOCK, "test", 0, NULL,
+ VFU_DEV_TYPE_PCI);
+ if (p->v == NULL) {
+ return -1;
+ }
+ p->rs = ROUND_UP(sizeof(struct vfio_device_migration_info), sysconf(_SC_PAGE_SIZE));
+ p->ds = sysconf(_SC_PAGE_SIZE);
+ p->s = p->rs + p->ds;
+ *state = p;
+ return setup(state);
+}
+
+static vfu_ctx_t *
+get_vfu_ctx(void **state)
+{
+ return (*((struct test_setup_migr_reg_dat **)(state)))->v;
+}
+
+static int
+teardown_test_setup_migration_region(void **state)
+{
+ struct test_setup_migr_reg_dat *p = *state;
+ vfu_destroy_ctx(p->v);
+ return 0;
+}
+
+static void
+test_setup_migration_region_too_small(void **state)
+{
+ vfu_ctx_t *v = get_vfu_ctx(state);
+ int r = vfu_setup_region(v, VFU_PCI_DEV_MIGR_REGION_IDX,
+ vfu_get_migr_register_area_size() - 1, NULL,
+ VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1);
+ assert_int_equal(-1, r);
+ assert_int_equal(EINVAL, errno);
+}
+
+static void
+test_setup_migration_region_size_ok(void **state)
+{
+ vfu_ctx_t *v = get_vfu_ctx(state);
+ int r = vfu_setup_region(v, VFU_PCI_DEV_MIGR_REGION_IDX,
+ vfu_get_migr_register_area_size(), NULL,
+ VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1);
+ assert_int_equal(0, r);
+}
+
+static void
+test_setup_migration_region_fully_mappable(void **state)
+{
+ struct test_setup_migr_reg_dat *p = *state;
+ int r = vfu_setup_region(p->v, VFU_PCI_DEV_MIGR_REGION_IDX, p->s,
+ NULL, VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0,
+ 0xdeadbeef);
+ assert_int_equal(-1, r);
+ assert_int_equal(EINVAL, errno);
+}
+
+static void
+test_setup_migration_region_sparsely_mappable_over_migration_registers(void **state)
+{
+ struct test_setup_migr_reg_dat *p = *state;
+ struct iovec mmap_areas[] = {
+ [0] = {
+ .iov_base = 0,
+ .iov_len = p->rs
+ }
+ };
+ int r = vfu_setup_region(p->v, VFU_PCI_DEV_MIGR_REGION_IDX, p->s, NULL,
+ VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, mmap_areas, 1, 0xdeadbeef);
+ assert_int_equal(-1, r);
+ assert_int_equal(EINVAL, errno);
+}
+
+static void
+test_setup_migration_region_sparsely_mappable_valid(void **state)
+{
+ struct test_setup_migr_reg_dat *p = *state;
+ struct iovec mmap_areas[] = {
+ [0] = {
+ .iov_base = (void *)p->rs,
+ .iov_len = p->ds
+ }
+ };
+ int r = vfu_setup_region(p->v, VFU_PCI_DEV_MIGR_REGION_IDX, p->s, NULL,
+ VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, mmap_areas, 1,
+ 0xdeadbeef);
+ assert_int_equal(0, r);
+}
+
+static void
+test_setup_migration_callbacks_without_migration_region(void **state)
+{
+ struct test_setup_migr_reg_dat *p = *state;
+ assert_int_equal(-1, vfu_setup_device_migration_callbacks(p->v, &p->c, 0));
+ assert_int_equal(EINVAL, errno);
+}
+
+static void
+test_setup_migration_callbacks_bad_data_offset(void **state)
+{
+ struct test_setup_migr_reg_dat *p = *state;
+ int r = vfu_setup_region(p->v, VFU_PCI_DEV_MIGR_REGION_IDX, p->s, NULL,
+ VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1);
+ assert_int_equal(0, r);
+ r = vfu_setup_device_migration_callbacks(p->v, &p->c,
+ vfu_get_migr_register_area_size() - 1);
+ assert_int_equal(-1, r);
+}
+
+static void
+test_setup_migration_callbacks(void **state)
+{
+ struct test_setup_migr_reg_dat *p = *state;
+ int r = vfu_setup_region(p->v, VFU_PCI_DEV_MIGR_REGION_IDX, p->s, NULL,
+ VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1);
+ assert_int_equal(0, r);
+ r = vfu_setup_device_migration_callbacks(p->v, &p->c,
+ vfu_get_migr_register_area_size());
+ assert_int_equal(0, r);
+ assert_non_null(p->v->migration);
+ /* FIXME can't validate p->v->migration because it's a private strcut, need to move it out of lib/migration.c */
+}
+
int main(void)
{
const struct CMUnitTest tests[] = {
@@ -1232,7 +1380,31 @@ int main(void)
cmocka_unit_test_setup(test_dma_map_sg, setup),
cmocka_unit_test_setup(test_dma_addr_to_sg, setup),
cmocka_unit_test_setup(test_vfu_setup_device_dma_cb, setup),
- cmocka_unit_test_setup(test_migration_state_transitions, setup)
+ cmocka_unit_test_setup(test_migration_state_transitions, setup),
+ cmocka_unit_test_setup_teardown(test_setup_migration_region_too_small,
+ setup_test_setup_migration_region,
+ teardown_test_setup_migration_region),
+ cmocka_unit_test_setup_teardown(test_setup_migration_region_size_ok,
+ setup_test_setup_migration_region,
+ teardown_test_setup_migration_region),
+ cmocka_unit_test_setup_teardown(test_setup_migration_region_fully_mappable,
+ setup_test_setup_migration_region,
+ teardown_test_setup_migration_region),
+ cmocka_unit_test_setup_teardown(test_setup_migration_region_sparsely_mappable_over_migration_registers,
+ setup_test_setup_migration_region,
+ teardown_test_setup_migration_region),
+ cmocka_unit_test_setup_teardown(test_setup_migration_region_sparsely_mappable_valid,
+ setup_test_setup_migration_region,
+ teardown_test_setup_migration_region),
+ cmocka_unit_test_setup_teardown(test_setup_migration_callbacks_without_migration_region,
+ setup_test_setup_migration_region,
+ teardown_test_setup_migration_region),
+ cmocka_unit_test_setup_teardown(test_setup_migration_callbacks_bad_data_offset,
+ setup_test_setup_migration_region,
+ teardown_test_setup_migration_region),
+ cmocka_unit_test_setup_teardown(test_setup_migration_callbacks,
+ setup_test_setup_migration_region,
+ teardown_test_setup_migration_region),
};
return cmocka_run_group_tests(tests, NULL, NULL);