diff options
author | Thanos Makatos <thanos.makatos@nutanix.com> | 2021-02-10 09:08:10 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-10 09:08:10 +0000 |
commit | 365ca96a97740332d3633090d850222d10bc9d70 (patch) | |
tree | bd9885cd57351c05ac12d63ef51a4e4e5b3eef7d /samples | |
parent | c5d11659c95c995acb77a71fe03c38b240ca43d9 (diff) | |
download | libvfio-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 'samples')
-rw-r--r-- | samples/client.c | 27 | ||||
-rw-r--r-- | samples/server.c | 84 |
2 files changed, 75 insertions, 36 deletions
diff --git a/samples/client.c b/samples/client.c index 92d86e0..d9b6661 100644 --- a/samples/client.c +++ b/samples/client.c @@ -209,7 +209,7 @@ send_device_reset(int sock) /* returns whether a VFIO migration capability is found */ static bool get_region_vfio_caps(struct vfio_info_cap_header *header, - struct vfio_region_info_cap_sparse_mmap *sparse) + struct vfio_region_info_cap_sparse_mmap **sparse) { struct vfio_region_info_cap_type *type; unsigned int i; @@ -218,12 +218,13 @@ get_region_vfio_caps(struct vfio_info_cap_header *header, while (true) { switch (header->id) { case VFIO_REGION_INFO_CAP_SPARSE_MMAP: - sparse = (struct vfio_region_info_cap_sparse_mmap*)header; + *sparse = (struct vfio_region_info_cap_sparse_mmap *)header; printf("%s: Sparse cap nr_mmap_areas %d\n", __func__, - sparse->nr_areas); - for (i = 0; i < sparse->nr_areas; i++) { + (*sparse)->nr_areas); + for (i = 0; i < (*sparse)->nr_areas; i++) { printf("%s: area %d offset %#llx size %llu\n", __func__, - i, sparse->areas[i].offset, sparse->areas[i].size); + i, (*sparse)->areas[i].offset, + (*sparse)->areas[i].size); } break; case VFIO_REGION_INFO_CAP_TYPE: @@ -261,14 +262,10 @@ do_get_device_region_info(int sock, struct vfio_region_info *region_info, } static void -mmap_sparse_areas(int *fds, size_t nr_fds, - struct vfio_region_info_cap_sparse_mmap *sparse) +mmap_sparse_areas(int *fds, struct vfio_region_info_cap_sparse_mmap *sparse) { size_t i; - assert(nr_fds == 2); - assert(sparse->nr_areas == 2); - for (i = 0; i < sparse->nr_areas; i++) { ssize_t ret; @@ -318,9 +315,6 @@ get_device_region_info(int sock, uint32_t index) region_info->index = index; do_get_device_region_info(sock, region_info, fds, &nr_fds); assert(region_info->argsz == size); - assert(nr_fds == 2); - assert(fds[0] >= 0); - assert(fds[1] >= 0); } else { nr_fds = 0; } @@ -332,9 +326,12 @@ get_device_region_info(int sock, uint32_t index) if (cap_sz) { struct vfio_region_info_cap_sparse_mmap *sparse = NULL; if (get_region_vfio_caps((struct vfio_info_cap_header*)(region_info + 1), - sparse)) { + &sparse)) { if (sparse != NULL) { - mmap_sparse_areas(fds, nr_fds, sparse); + assert((index == VFU_PCI_DEV_BAR1_REGION_IDX && nr_fds == 2) || + (index == VFU_PCI_DEV_MIGR_REGION_IDX && nr_fds == 1)); + assert(nr_fds == sparse->nr_areas); + mmap_sparse_areas(fds, sparse); } return true; } diff --git a/samples/server.c b/samples/server.c index 6170753..9c2ee37 100644 --- a/samples/server.c +++ b/samples/server.c @@ -398,20 +398,41 @@ migration_data_written(UNUSED vfu_ctx_t *vfu_ctx, UNUSED __u64 count) return 0; } +size_t +nr_pages(size_t size) +{ + return (size / sysconf(_SC_PAGE_SIZE) + (size % sysconf(_SC_PAGE_SIZE) > 1)); +} + +size_t +page_align(size_t size) { + return nr_pages(size) * sysconf(_SC_PAGE_SIZE); +} + int main(int argc, char *argv[]) { int ret; bool verbose = false; char opt; struct sigaction act = {.sa_handler = _sa_handler}; - size_t bar1_size = 0x3000; + const size_t bar1_size = 0x3000; + size_t migr_regs_size, migr_data_size, migr_size; struct server_data server_data = { .migration = { .state = VFU_MIGR_STATE_RUNNING } }; vfu_ctx_t *vfu_ctx; - FILE *fp; + FILE *bar1_fp, *migr_fp; + const vfu_migration_callbacks_t migr_callbacks = { + .version = VFU_MIGR_CALLBACKS_VERS, + .transition = &migration_device_state_transition, + .get_pending_bytes = &migration_get_pending_bytes, + .prepare_data = &migration_prepare_data, + .read_data = &migration_read_data, + .data_written = &migration_data_written, + .write_data = &migration_write_data + }; while ((opt = getopt(argc, argv, "v")) != -1) { switch (opt) { @@ -463,25 +484,26 @@ int main(int argc, char *argv[]) * this under Linux. If we really want to prohibit it we have to use * separate files for the same region. */ - if ((fp = tmpfile()) == NULL) { + if ((bar1_fp = tmpfile()) == NULL) { err(EXIT_FAILURE, "failed to create BAR1 file"); } server_data.bar1_size = bar1_size; - if (ftruncate(fileno(fp), server_data.bar1_size) == -1) { + if (ftruncate(fileno(bar1_fp), server_data.bar1_size) == -1) { err(EXIT_FAILURE, "failed to truncate BAR1 file"); } server_data.bar1 = mmap(NULL, server_data.bar1_size, PROT_READ | PROT_WRITE, - MAP_SHARED, fileno(fp), 0); + MAP_SHARED, fileno(bar1_fp), 0); if (server_data.bar1 == MAP_FAILED) { err(EXIT_FAILURE, "failed to mmap BAR1"); } - struct iovec mmap_areas[] = { + struct iovec bar1_mmap_areas[] = { { .iov_base = (void*)0, .iov_len = 0x1000 }, { .iov_base = (void*)0x2000, .iov_len = 0x1000 } }; ret = vfu_setup_region(vfu_ctx, VFU_PCI_DEV_BAR1_REGION_IDX, server_data.bar1_size, &bar1_access, - VFU_REGION_FLAG_RW, mmap_areas, 2, fileno(fp)); + VFU_REGION_FLAG_RW, bar1_mmap_areas, 2, + fileno(bar1_fp)); if (ret < 0) { err(EXIT_FAILURE, "failed to setup BAR1 region"); } @@ -501,21 +523,41 @@ int main(int argc, char *argv[]) err(EXIT_FAILURE, "failed to setup irq counts"); } - vfu_migration_t migration = { - .size = bar1_size + sizeof(time_t), - .mmap_areas = mmap_areas, - .nr_mmap_areas = 2, - .callbacks = { - .transition = &migration_device_state_transition, - .get_pending_bytes = &migration_get_pending_bytes, - .prepare_data = &migration_prepare_data, - .read_data = &migration_read_data, - .data_written = &migration_data_written, - .write_data = &migration_write_data - } - }; + /* setup migration */ - ret = vfu_setup_device_migration(vfu_ctx, &migration); + /* + * The migration registers aren't memory mappable, so in order to make the + * rest of the migration region memory mappable we must effectively reserve + * an entire page. + */ + migr_regs_size = vfu_get_migr_register_area_size(); + migr_data_size = page_align(bar1_size + sizeof(time_t)); + migr_size = migr_regs_size + migr_data_size; + if ((migr_fp = tmpfile()) == NULL) { + err(EXIT_FAILURE, "failed to create migration file"); + } + if (ftruncate(fileno(migr_fp), migr_size) == -1) { + err(EXIT_FAILURE, "failed to truncate migration file"); + } + server_data.bar1 = mmap(NULL, server_data.bar1_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fileno(bar1_fp), 0); + if (server_data.bar1 == MAP_FAILED) { + err(EXIT_FAILURE, "failed to mmap migration file"); + } + struct iovec migr_mmap_areas[] = { + [0] = { + .iov_base = (void *)migr_regs_size, + .iov_len = migr_data_size + }, + }; + ret = vfu_setup_region(vfu_ctx, VFU_PCI_DEV_MIGR_REGION_IDX, migr_size, + NULL, VFU_REGION_FLAG_RW, migr_mmap_areas, + ARRAY_SIZE(migr_mmap_areas), fileno(migr_fp)); + if (ret < 0) { + err(EXIT_FAILURE, "failed to setup migration region"); + } + ret = vfu_setup_device_migration_callbacks(vfu_ctx, &migr_callbacks, + migr_regs_size); if (ret < 0) { err(EXIT_FAILURE, "failed to setup device migration"); } |