diff options
-rw-r--r-- | lib/libmuser.c | 130 | ||||
-rw-r--r-- | samples/client.c | 75 |
2 files changed, 168 insertions, 37 deletions
diff --git a/lib/libmuser.c b/lib/libmuser.c index 8806d98..80f5b39 100644 --- a/lib/libmuser.c +++ b/lib/libmuser.c @@ -926,19 +926,17 @@ dev_get_irqinfo(lm_ctx_t *lm_ctx, struct vfio_irq_info *irq_info) /* * Populate the sparse mmap capability information to vfio-client. - * kernel/muser constructs the response for VFIO_DEVICE_GET_REGION_INFO - * accommodating sparse mmap information. * Sparse mmap information stays after struct vfio_region_info and cap_offest * points accordingly. */ static int dev_get_sparse_mmap_cap(lm_ctx_t *lm_ctx, lm_reg_info_t *lm_reg, - struct vfio_region_info *vfio_reg) + struct vfio_region_info **vfio_reg, bool is_kernel) { struct vfio_region_info_cap_sparse_mmap *sparse = NULL; struct lm_sparse_mmap_areas *mmap_areas; int nr_mmap_areas, i; - size_t size; + size_t sparse_size; ssize_t ret; if (lm_reg->mmap_areas == NULL) { @@ -947,24 +945,24 @@ dev_get_sparse_mmap_cap(lm_ctx_t *lm_ctx, lm_reg_info_t *lm_reg, } nr_mmap_areas = lm_reg->mmap_areas->nr_mmap_areas; - size = sizeof(*sparse) + (nr_mmap_areas * sizeof(*sparse->areas)); + sparse_size = sizeof(*sparse) + (nr_mmap_areas * sizeof(*sparse->areas)); /* * If vfio_reg does not have enough space to accommodate sparse info then - * set the argsz with the expected size and return. Vfio client will call - * back after reallocating the vfio_reg + * set the argsz with the expected size and return. This behaviour + * is only for kernel/muser.ko, where the request comes from kernel/vfio. */ - if (vfio_reg->argsz < size + sizeof(*vfio_reg)) { - lm_log(lm_ctx, LM_DBG, "vfio_reg too small=%d\n", vfio_reg->argsz); - vfio_reg->argsz = size + sizeof(*vfio_reg); - vfio_reg->cap_offset = 0; + if ((*vfio_reg)->argsz < sparse_size + sizeof(**vfio_reg) && is_kernel) { + lm_log(lm_ctx, LM_DBG, "vfio_reg too small=%d\n", (*vfio_reg)->argsz); + (*vfio_reg)->argsz = sparse_size + sizeof(**vfio_reg); + (*vfio_reg)->cap_offset = 0; return 0; } - lm_log(lm_ctx, LM_DBG, "%s: size %llu, nr_mmap_areas %u\n", __func__, size, - nr_mmap_areas); - sparse = calloc(1, size); + lm_log(lm_ctx, LM_DBG, "%s: size %llu, nr_mmap_areas %u\n", __func__, + sparse_size, nr_mmap_areas); + sparse = calloc(1, sparse_size); if (sparse == NULL) return -ENOMEM; sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP; @@ -976,18 +974,30 @@ dev_get_sparse_mmap_cap(lm_ctx_t *lm_ctx, lm_reg_info_t *lm_reg, for (i = 0; i < nr_mmap_areas; i++) { sparse->areas[i].offset = mmap_areas->areas[i].start; sparse->areas[i].size = mmap_areas->areas[i].size; + lm_log(lm_ctx, LM_DBG, "%s: nr %d offset %lu size\n", __func__, i, + sparse->areas[i].offset, sparse->areas[i].size); } - /* write the sparse mmap cap info to vfio-client user pages */ - ret = write(lm_ctx->conn_fd, sparse, size); - if (ret != (ssize_t)size) { - free(sparse); - return -EIO; + if (is_kernel) { + /* write the sparse mmap cap info to vfio-client user pages */ + ret = write(lm_ctx->conn_fd, sparse, sparse_size); + if (ret != (ssize_t)sparse_size) { + free(sparse); + return -EIO; + } + } else { + *vfio_reg = realloc(*vfio_reg, sparse_size + sizeof(**vfio_reg)); + if (*vfio_reg == NULL) { + goto out; + } + memcpy(*vfio_reg + sizeof(**vfio_reg), sparse, sparse_size); + (*vfio_reg)->argsz = sparse_size + sizeof(**vfio_reg); } - vfio_reg->flags |= VFIO_REGION_INFO_FLAG_MMAP | VFIO_REGION_INFO_FLAG_CAPS; - vfio_reg->cap_offset = sizeof(*vfio_reg); + (*vfio_reg)->flags |= VFIO_REGION_INFO_FLAG_MMAP | VFIO_REGION_INFO_FLAG_CAPS; + (*vfio_reg)->cap_offset = sizeof(**vfio_reg); +out: free(sparse); return 0; } @@ -1036,36 +1046,39 @@ dump_buffer(const char *prefix, const char *buf, uint32_t count) #endif static long -dev_get_reginfo(lm_ctx_t *lm_ctx, struct vfio_region_info *vfio_reg) +dev_get_reginfo(lm_ctx_t *lm_ctx, struct vfio_region_info **vfio_reg, + bool is_kernel) { lm_reg_info_t *lm_reg; int err; assert(lm_ctx != NULL); - assert(vfio_reg != NULL); - lm_reg = &lm_ctx->pci_info.reg_info[vfio_reg->index]; + assert(*vfio_reg != NULL); + lm_reg = &lm_ctx->pci_info.reg_info[(*vfio_reg)->index]; // Ensure provided argsz is sufficiently big and index is within bounds. - if ((vfio_reg->argsz < sizeof(struct vfio_region_info)) || - (vfio_reg->index >= LM_DEV_NUM_REGS)) { - lm_log(lm_ctx, LM_DBG, "bad args argsz=%d index=%d\n", vfio_reg->argsz, - vfio_reg->index); + if (((*vfio_reg)->argsz < sizeof(struct vfio_region_info)) || + ((*vfio_reg)->index >= LM_DEV_NUM_REGS)) { + lm_log(lm_ctx, LM_DBG, "bad args argsz=%d index=%d\n", + (*vfio_reg)->argsz, (*vfio_reg)->index); return -EINVAL; } - vfio_reg->offset = region_to_offset(vfio_reg->index); - vfio_reg->flags = lm_reg->flags; - vfio_reg->size = lm_reg->size; + (*vfio_reg)->offset = region_to_offset((*vfio_reg)->index); + (*vfio_reg)->flags = lm_reg->flags; + (*vfio_reg)->size = lm_reg->size; if (lm_reg->mmap_areas != NULL) { - err = dev_get_sparse_mmap_cap(lm_ctx, lm_reg, vfio_reg); + err = dev_get_sparse_mmap_cap(lm_ctx, lm_reg, vfio_reg, is_kernel); if (err) { return err; } } - lm_log(lm_ctx, LM_DBG, "region_info[%d]\n", vfio_reg->index); - dump_buffer("", (char*)vfio_reg, sizeof *vfio_reg); + lm_log(lm_ctx, LM_DBG, "region_info[%d] offset %lu flags %#x size %llu " + "argsz %llu\n", + (*vfio_reg)->index, (*vfio_reg)->offset, (*vfio_reg)->flags, + (*vfio_reg)->size, (*vfio_reg)->argsz); return 0; } @@ -1090,6 +1103,7 @@ dev_get_info(struct vfio_device_info *dev_info) static long do_muser_ioctl(lm_ctx_t *lm_ctx, struct muser_cmd_ioctl *cmd_ioctl, void *data) { + struct vfio_region_info *reg_info; int err = -ENOTSUP; assert(lm_ctx != NULL); @@ -1098,7 +1112,8 @@ do_muser_ioctl(lm_ctx_t *lm_ctx, struct muser_cmd_ioctl *cmd_ioctl, void *data) err = dev_get_info(&cmd_ioctl->data.dev_info); break; case VFIO_DEVICE_GET_REGION_INFO: - err = dev_get_reginfo(lm_ctx, &cmd_ioctl->data.reg_info); + reg_info = &cmd_ioctl->data.reg_info; + err = dev_get_reginfo(lm_ctx, ®_info, true); break; case VFIO_DEVICE_GET_IRQ_INFO: err = dev_get_irqinfo(lm_ctx, &cmd_ioctl->data.irq_info); @@ -1666,6 +1681,39 @@ out: return ret; } +static int handle_device_get_region_info(lm_ctx_t *lm_ctx, + struct vfio_user_header *hdr, + struct vfio_region_info **dev_reg_info) +{ + struct vfio_region_info *reg_info; + int ret; + + reg_info = calloc(sizeof(*reg_info), 1); + if (reg_info == NULL) { + return -ENOMEM; + } + + if ((hdr->msg_size - sizeof(*hdr)) != sizeof(*reg_info)) { + free(reg_info); + return -EINVAL; + } + + ret = recv(lm_ctx->conn_fd, reg_info, sizeof(*reg_info), 0); + if (ret < 0) { + free(reg_info); + return -errno; + } + + ret = dev_get_reginfo(lm_ctx, ®_info, false); + if (ret < 0) { + free(reg_info); + return ret; + } + *dev_reg_info = reg_info; + + return 0; +} + static int handle_device_get_info(lm_ctx_t *lm_ctx, struct vfio_user_header *hdr, struct vfio_device_info *dev_info) @@ -1922,6 +1970,7 @@ process_request(lm_ctx_t *lm_ctx) int nr_fds; struct vfio_irq_info irq_info; struct vfio_device_info dev_info; + struct vfio_region_info *dev_reg_info = NULL; void *data = NULL; bool free_data = false; int len; @@ -1982,6 +2031,13 @@ process_request(lm_ctx_t *lm_ctx) len = dev_info.argsz; } break; + case VFIO_USER_DEVICE_GET_REGION_INFO: + ret = handle_device_get_region_info(lm_ctx, &hdr, &dev_reg_info); + if (ret == 0) { + data = (void *)dev_reg_info; + len = dev_reg_info->argsz; + } + break; case VFIO_USER_DEVICE_GET_IRQ_INFO: ret = handle_device_get_irq_info(lm_ctx, &hdr, &irq_info); if (ret == 0) { @@ -2015,6 +2071,10 @@ process_request(lm_ctx_t *lm_ctx) if (free_data) { free(data); } + + if (dev_reg_info) { + free(dev_reg_info); + } return ret; } diff --git a/samples/client.c b/samples/client.c index 23ffcbc..6e49ff2 100644 --- a/samples/client.c +++ b/samples/client.c @@ -104,6 +104,71 @@ out: return ret; } +static int +get_device_region_info(int sock, struct vfio_device_info *client_dev_info) +{ + struct vfio_region_info region_info, *region_info_recv; + struct vfio_region_info_cap_sparse_mmap *sparse; + struct vfio_user_header hdr; + uint16_t msg_id; + size_t cap_sz; + int regsz = sizeof(region_info); + int i, ret; + + region_info.argsz = sizeof(region_info); + msg_id = 1; + for (i = 0; i < client_dev_info->num_regions; i++) { + region_info.index = i; + ret = send_vfio_user_msg(sock, msg_id, false, + VFIO_USER_DEVICE_GET_REGION_INFO, ®ion_info, + sizeof(region_info), NULL, 0); + if (ret < 0) { + fprintf(stderr, "%s: failed to send message: %s\n", __func__, + strerror(-ret)); + return ret; + } + + ret = recv_vfio_user_msg(sock, &hdr, true, &msg_id, ®ion_info, + ®sz); + if (ret < 0) { + fprintf(stderr, "%s: failed to receive header: %s\n", __func__, + strerror(-ret)); + return ret; + } + + + cap_sz = region_info.argsz - sizeof(struct vfio_region_info); + fprintf(stdout, "%s: region_info[%d] offset %lu flags %#x size %llu " + "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); + 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: nr %d offset %lu size\n", __func__, + sparse->areas[i].offset, sparse->areas[i].size); + } + free(sparse); + } + msg_id++; + } +} + static int get_device_info(int sock, struct vfio_device_info *dev_info) { struct vfio_user_header hdr; @@ -115,14 +180,14 @@ static int get_device_info(int sock, struct vfio_device_info *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, dev_info_sz); if (ret < 0) { fprintf(stderr, "failed to get device info: %s\n", strerror(-ret)); return ret; } printf("devinfo: flags %#x, num_regions %d, num_irqs %d\n", - dev_info.flags, dev_info.num_regions, dev_info.num_irqs); + dev_info->flags, dev_info->num_regions, dev_info->num_irqs); return 0; } @@ -269,6 +334,12 @@ int main(int argc, char *argv[]) return ret; } + /* XXX VFIO_USER_DEVICE_GET_REGION_INFO */ + ret = get_device_region_info(sock, &client_dev_info); + if (ret < 0) { + return ret; + } + /* * XXX VFIO_USER_DMA_MAP * |