diff options
author | JAKelly10 <87753759+JAKelly10@users.noreply.github.com> | 2021-09-08 15:47:23 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-08 15:47:23 +0100 |
commit | d12584bf8c726a1907655f03e5a18b549599da41 (patch) | |
tree | 1f302bbedbe23805551127f74f1d3f8186a92fa2 /lib | |
parent | 080e664ed5e79dcf5ae5a36521cab0ddb7ebc5f0 (diff) | |
download | libvfio-user-d12584bf8c726a1907655f03e5a18b549599da41.zip libvfio-user-d12584bf8c726a1907655f03e5a18b549599da41.tar.gz libvfio-user-d12584bf8c726a1907655f03e5a18b549599da41.tar.bz2 |
initial ioeventfd support (#601)
Provide initial support for handling VFIO_USER_DEVICE_GET_REGION_IO_FDS, along with a new vfu_create_ioeventfd() API.
Reviewed-by: John Levon <john.levon@nutanix.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libvfio-user.c | 182 | ||||
-rw-r--r-- | lib/private.h | 12 |
2 files changed, 193 insertions, 1 deletions
diff --git a/lib/libvfio-user.c b/lib/libvfio-user.c index 3cb30ba..8881dc7 100644 --- a/lib/libvfio-user.c +++ b/lib/libvfio-user.c @@ -48,6 +48,8 @@ #include <sys/socket.h> #include <sys/stat.h> +#include <sys/eventfd.h> + #include "dma.h" #include "irq.h" #include "libvfio-user.h" @@ -460,6 +462,179 @@ handle_device_get_region_info(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg) return 0; } +EXPORT int +vfu_create_ioeventfd(vfu_ctx_t *vfu_ctx, uint32_t region_idx, int fd, + size_t offset, uint32_t size, uint32_t flags, + uint64_t datamatch) +{ + vfu_reg_info_t *vfu_reg = &vfu_ctx->reg_info[region_idx]; + + assert(vfu_ctx != NULL); + assert(fd >= 0); + + if (region_idx >= VFU_PCI_DEV_NUM_REGIONS) { + return ERROR_INT(EINVAL); + } + + vfu_reg = &vfu_ctx->reg_info[region_idx]; + + if (offset + size > vfu_reg->size) { + return ERROR_INT(EINVAL); + } + + ioeventfd_t *elem = malloc(sizeof(ioeventfd_t)); + if (elem == NULL) { + return -1; + } + + elem->fd = fd; + elem->offset = offset; + elem->size = size; + elem->flags = flags; + elem->datamatch = datamatch; + LIST_INSERT_HEAD(&vfu_reg->subregions, elem, entry); + + return 0; +} + +static void +free_regions(vfu_ctx_t *vfu_ctx) +{ + size_t index = 0; + + assert(vfu_ctx != NULL); + + for (index = 0; index < VFU_PCI_DEV_NUM_REGIONS; index++) { + vfu_reg_info_t *vfu_reg = &vfu_ctx->reg_info[index]; + + while (!LIST_EMPTY(&vfu_reg->subregions)) { + ioeventfd_t *n = LIST_FIRST(&vfu_reg->subregions); + LIST_REMOVE(n, entry); + free(n); + } + } + free(vfu_ctx->reg_info); +} + +/* + * This function is used to add fd's to the fd return array and gives you back + * the index of the fd that has been added. If the fd is already present it will + * return the index to that duplicate fd to reduce the number of fd's sent. + */ +static int +add_fd_index(int *out_fds, size_t *nr_out_fds, int fd_search) +{ + size_t i = 0; + + assert(out_fds != NULL); + assert(nr_out_fds != NULL); + + for (i = 0; i < *nr_out_fds; i++) { + if (out_fds[i] == fd_search) { + return i; + } + } + + out_fds[*nr_out_fds] = fd_search; + (*nr_out_fds)++; + + return *nr_out_fds - 1; +} + +static int +handle_device_get_region_io_fds(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg) +{ + size_t max_sent_sub_regions = 0; + uint subregion_array_size = 0; + vfu_reg_info_t *vfu_reg = NULL; + vfio_user_region_io_fds_reply_t *reply = NULL; + vfio_user_sub_region_ioeventfd_t *ioefd = NULL; + vfio_user_region_io_fds_request_t *req = NULL; + ioeventfd_t *sub_reg = NULL; + size_t nr_sub_reg = 0; + size_t i = 0; + + assert(vfu_ctx != NULL); + assert(msg != NULL); + assert(msg->out_fds == NULL); + + if (msg->in_size != sizeof(vfio_user_region_io_fds_request_t)) { + return ERROR_INT(EINVAL); + } + + req = msg->in_data; + + if (req->flags != 0 || req->count != 0) { + return ERROR_INT(EINVAL); + } + + if (req->index >= vfu_ctx->nr_regions) { + vfu_log(vfu_ctx, LOG_DEBUG, "bad region index %d in get region io fds " + "info", req->index); + return ERROR_INT(EINVAL); + } + + vfu_reg = &vfu_ctx->reg_info[req->index]; + + if (vfu_reg->fd == -1) { + return ERROR_INT(EINVAL); + } + + LIST_FOREACH(sub_reg, &vfu_reg->subregions, entry) { + nr_sub_reg++; + } + + if (req->argsz < sizeof(vfio_user_region_io_fds_reply_t) || + req->argsz > SERVER_MAX_DATA_XFER_SIZE) { + return ERROR_INT(EINVAL); + } + + max_sent_sub_regions = MIN((req->argsz - + sizeof(vfio_user_region_io_fds_reply_t)) / + sizeof(vfio_user_sub_region_ioeventfd_t), + nr_sub_reg); + subregion_array_size = ((max_sent_sub_regions >= nr_sub_reg) ? nr_sub_reg : + 0) * sizeof(vfio_user_sub_region_ioeventfd_t); + msg->out_size = sizeof(vfio_user_region_io_fds_reply_t) + + subregion_array_size; + msg->out_data = calloc(1, msg->out_size); + if (msg->out_data == NULL) { + return -1; + } + reply = msg->out_data; + reply->index = req->index; + reply->count = nr_sub_reg; + reply->flags = 0; + reply->argsz = sizeof(vfio_user_region_io_fds_reply_t) + + nr_sub_reg * + sizeof(vfio_user_sub_region_ioeventfd_t); + + msg->nr_out_fds = 0; + if (req->argsz >= reply->argsz) { + msg->out_fds = calloc(sizeof(int), max_sent_sub_regions); + if (msg->out_fds == NULL) { + return -1; + } + + sub_reg = LIST_FIRST(&vfu_reg->subregions); + for (i = 0; i < max_sent_sub_regions; i++) { + + ioefd = &reply->sub_regions[i].ioeventfd; + ioefd->offset = sub_reg->offset; + ioefd->size = sub_reg->size; + ioefd->fd_index = add_fd_index(msg->out_fds, &msg->nr_out_fds, + sub_reg->fd); + ioefd->type = VFIO_USER_IO_FD_TYPE_IOEVENTFD; + ioefd->flags = sub_reg->flags; + ioefd->datamatch = sub_reg->datamatch; + + sub_reg = LIST_NEXT(sub_reg, entry); + } + } + + return 0; +} + int consume_fd(int *fds, size_t nr_fds, size_t index) { @@ -992,6 +1167,10 @@ exec_command(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg) ret = handle_device_get_region_info(vfu_ctx, msg); break; + case VFIO_USER_DEVICE_GET_REGION_IO_FDS: + ret = handle_device_get_region_io_fds(vfu_ctx, msg); + break; + case VFIO_USER_DEVICE_GET_IRQ_INFO: ret = handle_device_get_irq_info(vfu_ctx, msg); break; @@ -1264,7 +1443,7 @@ vfu_destroy_ctx(vfu_ctx_t *vfu_ctx) dma_controller_destroy(vfu_ctx->dma); } free_sparse_mmap_areas(vfu_ctx); - free(vfu_ctx->reg_info); + free_regions(vfu_ctx); free(vfu_ctx->migration); free(vfu_ctx->irqs); free(vfu_ctx); @@ -1328,6 +1507,7 @@ vfu_create_ctx(vfu_trans_t trans, const char *path, int flags, void *pvt, for (i = 0; i < vfu_ctx->nr_regions; i++) { vfu_ctx->reg_info[i].fd = -1; + LIST_INIT(&vfu_ctx->reg_info[i].subregions); } if (vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_ERR_IRQ, 1) == -1) { diff --git a/lib/private.h b/lib/private.h index fb26b6a..93a354b 100644 --- a/lib/private.h +++ b/lib/private.h @@ -34,6 +34,7 @@ #define LIB_VFIO_USER_PRIVATE_H #include <errno.h> +#include <sys/queue.h> #include "common.h" #include "pci_caps.h" @@ -133,6 +134,8 @@ typedef struct { int fd; /* offset of region within fd. */ uint64_t offset; + /* The subregions for ioregionfds and ioeventfds */ + LIST_HEAD(, ioeventfd) subregions; } vfu_reg_info_t; struct pci_dev { @@ -173,6 +176,15 @@ struct vfu_ctx { vfu_dev_type_t dev_type; }; +typedef struct ioeventfd { + uint64_t offset; + uint64_t size; + int32_t fd; + uint32_t flags; + uint64_t datamatch; + LIST_ENTRY(ioeventfd) entry; +} ioeventfd_t; + static inline int ERROR_INT(int err) { |