aboutsummaryrefslogtreecommitdiff
path: root/samples/client.c
diff options
context:
space:
mode:
authorThanos Makatos <thanos.makatos@nutanix.com>2020-10-29 17:57:08 -0400
committerThanos Makatos <thanos.makatos@nutanix.com>2020-10-29 17:57:08 -0400
commitfe27b18d7c20064281633eac541752e6ef6e8ada (patch)
tree7ab968a73ab30a1bb00de3d735df8e719ee177a1 /samples/client.c
parent05a1d6d9bc63370e59fe7fd7c5e8acd57249e315 (diff)
downloadlibvfio-user-fe27b18d7c20064281633eac541752e6ef6e8ada.zip
libvfio-user-fe27b18d7c20064281633eac541752e6ef6e8ada.tar.gz
libvfio-user-fe27b18d7c20064281633eac541752e6ef6e8ada.tar.bz2
support for live migration region and dirty page logging
This patch adds support for the live migration region and dirty page logging following VFIO. Live migration is NOT yet functional as handling accesses to the migration region is not yet implemented. Currenty the live migration region is fixed at index 9 simply for simplifying the implementation. Dirty page logging is simplified by requiring IOVA ranges to match exactly the entire IOVA range. Signed-off-by: Thanos Makatos <thanos.makatos@nutanix.com>
Diffstat (limited to 'samples/client.c')
-rw-r--r--samples/client.c252
1 files changed, 193 insertions, 59 deletions
diff --git a/samples/client.c b/samples/client.c
index 925335d..5ff79cd 100644
--- a/samples/client.c
+++ b/samples/client.c
@@ -39,6 +39,7 @@
#include <time.h>
#include <err.h>
#include <assert.h>
+//#include <sys/uio.h>
#include "../lib/muser.h"
#include "../lib/muser_priv.h"
@@ -66,13 +67,16 @@ init_sock(const char *path)
}
static int
-set_version(int sock, int client_max_fds, int *server_max_fds)
+set_version(int sock, int client_max_fds, int *server_max_fds, size_t *pgsize)
{
int ret, mj, mn;
uint16_t msg_id;
char *client_caps = NULL;
- ret = recv_version(sock, &mj, &mn, &msg_id, false, server_max_fds);
+ assert(server_max_fds != NULL);
+ assert(pgsize != NULL);
+
+ ret = recv_version(sock, &mj, &mn, &msg_id, false, server_max_fds, pgsize);
if (ret < 0) {
fprintf(stderr, "failed to receive version from server: %s\n",
strerror(-ret));
@@ -85,7 +89,8 @@ set_version(int sock, int client_max_fds, int *server_max_fds)
goto out;
}
- ret = asprintf(&client_caps, "{max_fds: %d}", client_max_fds);
+ ret = asprintf(&client_caps, "{max_fds: %d, migration: {pgsize: %lu}}",
+ client_max_fds, sysconf(_SC_PAGESIZE));
if (ret == -1) {
client_caps = NULL;
ret = -ENOMEM; /* FIXME */
@@ -115,14 +120,64 @@ send_device_reset(int sock)
}
static int
+get_region_vfio_caps(int sock, size_t cap_sz)
+{
+ struct vfio_info_cap_header *header, *_header;
+ struct vfio_region_info_cap_type *type;
+ struct vfio_region_info_cap_sparse_mmap *sparse;
+ int i, ret;
+
+ header = _header = calloc(cap_sz, 1);
+ if (header == NULL) {
+ return -ENOMEM;
+ }
+
+ ret = recv(sock, header, cap_sz, 0);
+ if (ret < 0) {
+ err(EXIT_FAILURE, "failed to receive VFIO cap info");
+ }
+ assert(ret == cap_sz);
+
+ while (true) {
+ switch (header->id) {
+ case VFIO_REGION_INFO_CAP_SPARSE_MMAP:
+ sparse = (struct vfio_region_info_cap_sparse_mmap*)header;
+ fprintf(stdout, "%s: Sparse cap nr_mmap_areas %d\n", __func__,
+ sparse->nr_areas);
+ for (i = 0; i < sparse->nr_areas; i++) {
+ fprintf(stdout, "%s: area %d offset %#lx size %llu\n", __func__,
+ i, sparse->areas[i].offset, sparse->areas[i].size);
+ }
+ break;
+ case VFIO_REGION_INFO_CAP_TYPE:
+ type = (struct vfio_region_info_cap_type*)header;
+ if (type->type != VFIO_REGION_TYPE_MIGRATION ||
+ type->subtype != VFIO_REGION_SUBTYPE_MIGRATION) {
+ fprintf(stderr, "bad region type %d/%d\n", type->type,
+ type->subtype);
+ exit(EXIT_FAILURE);
+ }
+ printf("migration region\n");
+ break;
+ default:
+ fprintf(stderr, "bad VFIO cap ID %#x\n", header->id);
+ exit(EXIT_FAILURE);
+ }
+ if (header->next == 0) {
+ break;
+ }
+ header = (struct vfio_info_cap_header*)((char*)header + header->next - sizeof(struct vfio_region_info));
+ }
+ free(_header);
+}
+
+static int
get_device_region_info(int sock, struct vfio_device_info *client_dev_info)
{
struct vfio_region_info region_info;
- struct vfio_region_info_cap_sparse_mmap *sparse;
struct vfio_user_header hdr;
uint16_t msg_id = 0;
size_t cap_sz;
- int regsz = sizeof(region_info);
int i, ret;
msg_id = 1;
@@ -133,8 +188,9 @@ get_device_region_info(int sock, struct vfio_device_info *client_dev_info)
msg_id++;
ret = send_recv_vfio_user_msg(sock, msg_id,
VFIO_USER_DEVICE_GET_REGION_INFO,
- &region_info, regsz, NULL, 0, NULL,
- &region_info, regsz);
+ &region_info, sizeof region_info,
+ NULL, 0, NULL,
+ &region_info, sizeof(region_info));
if (ret < 0) {
fprintf(stderr, "failed to get device region info: %s\n",
strerror(-ret));
@@ -146,44 +202,26 @@ get_device_region_info(int sock, struct vfio_device_info *client_dev_info)
"cap_sz %d\n", __func__, i, region_info.offset,
region_info.flags, region_info.size, cap_sz);
if (cap_sz) {
- int j;
-
- sparse = calloc(cap_sz, 1);
- if (sparse == NULL) {
- return -ENOMEM;
- }
-
- ret = recv(sock, sparse, cap_sz, 0);
- if (ret < 0) {
- ret = -errno;
- fprintf(stderr, "%s: failed to receive sparse cap info: %s\n",
- __func__, strerror(-ret));
- free(sparse);
+ ret = get_region_vfio_caps(sock, cap_sz);
+ if (ret != 0) {
return ret;
}
- fprintf(stdout, "%s: Sparse cap nr_mmap_areas %d\n", __func__,
- sparse->nr_areas);
- for (j = 0; j < sparse->nr_areas; j++) {
- fprintf(stdout, "%s: area %d offset %#lx size %llu\n", __func__,
- j, sparse->areas[j].offset, sparse->areas[j].size);
- }
- free(sparse);
}
}
+ return 0;
}
static int get_device_info(int sock, struct vfio_device_info *dev_info)
{
struct vfio_user_header hdr;
- int dev_info_sz = sizeof(*dev_info);
uint16_t msg_id;
int ret;
- dev_info->argsz = dev_info_sz;
+ dev_info->argsz = sizeof(*dev_info);
msg_id = 1;
ret = send_recv_vfio_user_msg(sock, msg_id, VFIO_USER_DEVICE_GET_INFO,
- dev_info, dev_info_sz, NULL, 0, NULL,
- dev_info, dev_info_sz);
+ dev_info, sizeof(*dev_info), NULL, 0, NULL,
+ dev_info, sizeof(*dev_info));
if (ret < 0) {
fprintf(stderr, "failed to get device info: %s\n", strerror(-ret));
return ret;
@@ -197,30 +235,35 @@ static int get_device_info(int sock, struct vfio_device_info *dev_info)
static int
configure_irqs(int sock)
{
- int i, size;
- int ret;
+ int i, ret;
+ size_t size;
struct vfio_irq_set irq_set;
- struct vfio_user_irq_info irq_info;
+ struct vfio_user_irq_info vfio_user_irq_info;
struct vfio_user_header hdr;
uint16_t msg_id = 1;
int irq_fd;
uint64_t val;
+ struct iovec iovecs[2];
- for (i = 0; i < LM_DEV_NUM_IRQS; i++) {
- struct vfio_irq_info irq_info = {.argsz = sizeof irq_info, .index = i};
+ for (i = 0; i < LM_DEV_NUM_IRQS; i++) { /* TODO move body of loop into function */
int size;
+ struct vfio_irq_info vfio_irq_info = {
+ .argsz = sizeof vfio_irq_info,
+ .index = i
+ };
ret = send_recv_vfio_user_msg(sock, msg_id,
VFIO_USER_DEVICE_GET_IRQ_INFO,
- &irq_info, sizeof irq_info, NULL, 0, NULL,
- &irq_info, sizeof irq_info);
+ &vfio_irq_info, sizeof vfio_irq_info,
+ NULL, 0, NULL,
+ &vfio_irq_info, sizeof vfio_irq_info);
if (ret < 0) {
fprintf(stderr, "failed to get %s info: %s\n", irq_to_str[i],
strerror(-ret));
return ret;
}
- if (irq_info.count > 0) {
+ if (vfio_irq_info.count > 0) {
printf("IRQ %s: count=%d flags=%#x\n",
- irq_to_str[i], irq_info.count, irq_info.flags);
+ irq_to_str[i], vfio_irq_info.count, vfio_irq_info.flags);
}
}
@@ -258,14 +301,16 @@ configure_irqs(int sock)
printf("INTx triggered!\n");
msg_id++;
- size = sizeof(irq_info);
- ret = recv_vfio_user_msg(sock, &hdr, false, &msg_id, &irq_info, &size);
+
+ size = sizeof(vfio_user_irq_info);
+ ret = recv_vfio_user_msg(sock, &hdr, false, &msg_id, &vfio_user_irq_info,
+ &size);
if (ret < 0) {
- fprintf(stderr, "failed to recieve IRQ message: %s\n", strerror(-ret));
+ fprintf(stderr, "failed to receive IRQ message: %s\n", strerror(-ret));
return ret;
}
- if (irq_info.subindex >= irq_set.count) {
- fprintf(stderr, "bad IRQ %d, max=%d\n", irq_info.subindex,
+ if (vfio_user_irq_info.subindex >= irq_set.count) {
+ fprintf(stderr, "bad IRQ %d, max=%d\n", vfio_user_irq_info.subindex,
irq_set.count);
return -ENOENT;
}
@@ -305,7 +350,10 @@ access_bar0(int sock)
fprintf(stderr, "failed to write to BAR0: %s\n", strerror(-ret));
return ret;
}
- assert(region_access.count == sizeof data.t);
+ if (region_access.count != sizeof data.t) {
+ fprintf(stderr, "bad written data length %d\n", region_access.count);
+ return -EINVAL;
+ }
printf("wrote to BAR0: %ld\n", data.t);
@@ -334,7 +382,8 @@ static int handle_dma_write(int sock, struct vfio_user_dma_region *dma_regions,
{
struct vfio_user_dma_region_access dma_access;
struct vfio_user_header hdr;
- int ret, size = sizeof(dma_access), i;
+ int ret, i;
+ size_t size = sizeof(dma_access);
uint16_t msg_id;
void *data;
@@ -371,9 +420,10 @@ static int handle_dma_write(int sock, struct vfio_user_dma_region *dma_regions,
dma_access.count = 0;
ret = send_vfio_user_msg(sock, msg_id, true, VFIO_USER_DMA_WRITE,
- &dma_access, sizeof(dma_access), NULL, 0);
+ &dma_access, sizeof dma_access, NULL, 0);
if (ret < 0) {
- fprintf(stderr, "failed to send reply of DMA write: %m\n");
+ fprintf(stderr, "failed to send reply of DMA write: %s\n",
+ strerror(-ret));
}
out:
@@ -386,7 +436,8 @@ static int handle_dma_read(int sock, struct vfio_user_dma_region *dma_regions,
{
struct vfio_user_dma_region_access dma_access, *response;
struct vfio_user_header hdr;
- int ret, size = sizeof(dma_access), i, response_sz;
+ int ret, i, response_sz;
+ size_t size = sizeof(dma_access);
uint16_t msg_id;
void *data;
@@ -449,6 +500,56 @@ static int handle_dma_io(int sock, struct vfio_user_dma_region *dma_regions,
return 0;
}
+static int
+get_dirty_bitmaps(int sock, struct vfio_user_dma_region *dma_regions,
+ int nr_dma_regions)
+{
+ struct vfio_iommu_type1_dirty_bitmap dirty_bitmap = {0};
+ struct vfio_iommu_type1_dirty_bitmap_get bitmaps[2];
+ int ret, i;
+ struct iovec iovecs[4] = {
+ [1] = {
+ .iov_base = &dirty_bitmap,
+ .iov_len = sizeof dirty_bitmap
+ }
+ };
+ struct vfio_user_header hdr = {0};
+ char data[ARRAY_SIZE(bitmaps)];
+
+ assert(dma_regions != NULL);
+ assert(nr_dma_regions >= ARRAY_SIZE(bitmaps));
+
+ for (i = 0; i < ARRAY_SIZE(bitmaps); i++) {
+ bitmaps[i].iova = dma_regions[i].addr;
+ bitmaps[i].size = dma_regions[i].size;
+ bitmaps[i].bitmap.size = 1; /* FIXME calculate based on page and IOVA size, don't hardcode */
+ bitmaps[i].bitmap.pgsize = sysconf(_SC_PAGESIZE);
+ iovecs[(i + 2)].iov_base = &bitmaps[i]; /* FIXME the +2 is because iovecs[0] is the vfio_user_header and iovecs[1] is vfio_iommu_type1_dirty_bitmap */
+ iovecs[(i + 2)].iov_len = sizeof(struct vfio_iommu_type1_dirty_bitmap_get);
+ }
+
+ /*
+ * FIXME there should be at least two IOVAs. Send single message for two
+ * IOVAs and ensure only one bit is set in first IOVA.
+ */
+ dirty_bitmap.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP;
+ ret = _send_recv_vfio_user_msg(sock, 0, VFIO_USER_DIRTY_PAGES,
+ iovecs, ARRAY_SIZE(iovecs),
+ NULL, 0,
+ &hdr, data, ARRAY_SIZE(data));
+ if (ret != 0) {
+ fprintf(stderr, "failed to start dirty page logging: %s\n",
+ strerror(-ret));
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bitmaps); i++) {
+ printf("%#x-%#x\t%hhu\n", bitmaps[i].iova,
+ bitmaps[i].iova + bitmaps[i].size - 1, data[i]);
+ }
+ return 0;
+}
+
int main(int argc, char *argv[])
{
int ret, sock;
@@ -461,7 +562,9 @@ int main(int argc, char *argv[])
int fd;
const int client_max_fds = 32;
int server_max_fds;
+ size_t pgsize;
int nr_dma_regions;
+ struct vfio_iommu_type1_dirty_bitmap dirty_bitmap = {0};
if (argc != 2) {
fprintf(stderr, "usage: %s /path/to/socket\n", argv[0]);
@@ -478,7 +581,7 @@ int main(int argc, char *argv[])
* The server proposes version upon connection, we need to send back the
* version the version we support.
*/
- if ((ret = set_version(sock, client_max_fds, &server_max_fds)) < 0) {
+ if ((ret = set_version(sock, client_max_fds, &server_max_fds, &pgsize)) < 0) {
return ret;
}
@@ -503,7 +606,7 @@ int main(int argc, char *argv[])
/*
* XXX VFIO_USER_DMA_MAP
*
- * Tell the server we have some DMA regions it can access. Each DMA regions
+ * Tell the server we have some DMA regions it can access. Each DMA region
* is accompanied by a file descriptor, so let's create more (2x) DMA
* regions that can fit in a message that can be handled by the server.
*/
@@ -531,10 +634,10 @@ int main(int argc, char *argv[])
for (i = 0; i < nr_dma_regions / server_max_fds; i++, msg_id++) {
ret = send_recv_vfio_user_msg(sock, msg_id, VFIO_USER_DMA_MAP,
- dma_regions + (i * server_max_fds),
- sizeof *dma_regions * server_max_fds,
- dma_region_fds + (i * server_max_fds),
- server_max_fds, NULL, NULL, 0);
+ dma_regions + (i * server_max_fds),
+ sizeof(*dma_regions) * server_max_fds,
+ dma_region_fds + (i * server_max_fds),
+ server_max_fds, NULL, NULL, 0);
if (ret < 0) {
fprintf(stderr, "failed to map DMA regions: %s\n", strerror(-ret));
return ret;
@@ -553,6 +656,17 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE);
}
+
+ dirty_bitmap.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_START;
+ ret = send_recv_vfio_user_msg(sock, 0, VFIO_USER_DIRTY_PAGES,
+ &dirty_bitmap, sizeof dirty_bitmap,
+ NULL, 0, NULL, NULL, 0);
+ if (ret != 0) {
+ fprintf(stderr, "failed to start dirty page logging: %s\n",
+ strerror(-ret));
+ exit(EXIT_FAILURE);
+ }
+
/*
* XXX VFIO_USER_DEVICE_GET_IRQ_INFO and VFIO_IRQ_SET_ACTION_TRIGGER
* Query interrupts, configure an eventfd to be associated with INTx, and
@@ -570,6 +684,23 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE);
}
+ ret = get_dirty_bitmaps(sock, dma_regions, nr_dma_regions);
+ if (ret < 0) {
+ fprintf(stderr, "failed to receive dirty bitmaps: %s\n",
+ strerror(-ret));
+ exit(EXIT_FAILURE);
+ }
+
+ dirty_bitmap.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP;
+ ret = send_recv_vfio_user_msg(sock, 0, VFIO_USER_DIRTY_PAGES,
+ &dirty_bitmap, sizeof dirty_bitmap,
+ NULL, 0, NULL, NULL, 0);
+ if (ret != 0) {
+ fprintf(stderr, "failed to stop dirty page logging: %s\n",
+ strerror(-ret));
+ exit(EXIT_FAILURE);
+ }
+
/*
* FIXME now that region read/write works, change the server implementation
* to trigger an interrupt after N seconds, where N is the value written to
@@ -577,7 +708,11 @@ int main(int argc, char *argv[])
*/
/* BAR1 can be memory mapped and read directly */
- /* TODO implement the following: write a value in BAR1, a server timer will increase it every second (SIGALARM) */
+
+ /*
+ * TODO implement the following: write a value in BAR1, a server timer will
+ * increase it every second (SIGALARM)
+ */
/*
* XXX VFIO_USER_DMA_UNMAP
@@ -585,8 +720,7 @@ int main(int argc, char *argv[])
* unmap the first group of the DMA regions
*/
ret = send_recv_vfio_user_msg(sock, msg_id, VFIO_USER_DMA_UNMAP,
- dma_regions,
- sizeof *dma_regions * server_max_fds,
+ dma_regions, sizeof *dma_regions * server_max_fds,
NULL, 0, NULL, NULL, 0);
if (ret < 0) {
fprintf(stderr, "failed to unmap DMA regions: %s\n", strerror(-ret));