diff options
Diffstat (limited to 'samples/client.c')
-rw-r--r-- | samples/client.c | 252 |
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, - ®ion_info, regsz, NULL, 0, NULL, - ®ion_info, regsz); + ®ion_info, sizeof region_info, + NULL, 0, NULL, + ®ion_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)); |