aboutsummaryrefslogtreecommitdiff
path: root/samples
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 /samples
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 'samples')
-rw-r--r--samples/client.c27
-rw-r--r--samples/server.c84
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");
}