Add client support for capability VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS (capability ID 16) in vfio-user.
Update vfio_user_device_io_get_region_info() to receive multiple file descriptors passed in SCM_RIGHTS ancillary data for a region, store them in VFIOUserProxy, and implement setup_sparse_mmaps in vfio_user_device_io_ops_sock to populate per-area file descriptors and fd_offset values. Signed-off-by: Naman Gulati <[email protected]> --- hw/vfio-user/protocol.h | 22 ++++++++++ hw/vfio-user/proxy.h | 2 + hw/vfio-user/device.c | 90 +++++++++++++++++++++++++++++++++++++---- hw/vfio-user/proxy.c | 32 +++++++++++++-- 4 files changed, 135 insertions(+), 11 deletions(-) diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h index c478d1353f..7b27a70db5 100644 --- a/hw/vfio-user/protocol.h +++ b/hw/vfio-user/protocol.h @@ -15,6 +15,8 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ +#define VFIO_USER_MAX_REGIONS 100 + typedef struct { uint16_t id; uint16_t command; @@ -166,6 +168,26 @@ typedef struct { uint64_t offset; } VFIOUserRegionInfo; +/* + * VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS + */ +#define VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS 16 + +struct vfio_region_sparse_mmap_fd_area { + uint64_t offset; + uint64_t fd_offset; + uint64_t size; + uint32_t fd_index; + uint32_t pad; +}; + +struct vfio_region_info_cap_sparse_mmap_fds { + struct vfio_info_cap_header header; + uint32_t nr_areas; + uint32_t reserved; + struct vfio_region_sparse_mmap_fd_area areas[]; +}; + /* * VFIO_USER_DEVICE_GET_IRQ_INFO * imported from struct vfio_irq_info diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h index 7b97460cc5..4b9834a436 100644 --- a/hw/vfio-user/proxy.h +++ b/hw/vfio-user/proxy.h @@ -72,6 +72,7 @@ typedef struct VFIOUserProxy { AioContext *ctx; QEMUBH *req_bh; bool async_ops; + VFIOUserFDs *region_fds[VFIO_USER_MAX_REGIONS]; /* * above only changed when BQL is held @@ -111,6 +112,7 @@ bool vfio_user_validate_version(VFIOUserProxy *proxy, Error **errp); VFIOUserFDs *vfio_user_getfds(int numfds); void vfio_user_putfds(VFIOUserMsg *msg); +void vfio_user_free_fds(VFIOUserFDs *fds); void vfio_user_disable_posted_writes(VFIOUserProxy *proxy); diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index b8d2b7c1a8..f70321c060 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -14,12 +14,14 @@ #include "hw/vfio-user/device.h" #include "hw/vfio-user/trace.h" +#include "hw/vfio/vfio-region.h" +#include "hw/vfio/vfio-helpers.h" +#include "hw/vfio/trace.h" /* * These are to defend against a malign server trying * to force us to run out of memory. */ -#define VFIO_USER_MAX_REGIONS 100 #define VFIO_USER_MAX_IRQS 50 bool vfio_user_get_device_info(VFIOUserProxy *proxy, @@ -172,14 +174,17 @@ static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev, struct vfio_region_info *info, int *fd) { - VFIOUserFDs fds = { 0, 1, fd}; - int ret; + int fds[VFIO_USER_MAX_MAX_FDS]; + VFIOUserFDs user_fds = { 0, VFIO_USER_MAX_MAX_FDS, fds }; + int i, ret; + + *fd = -1; if (info->index > vbasedev->num_initial_regions) { return -EINVAL; } - ret = vfio_user_get_region_info(vbasedev->proxy, info, &fds); + ret = vfio_user_get_region_info(vbasedev->proxy, info, &user_fds); if (ret) { return ret; } @@ -187,10 +192,29 @@ static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev, /* cap_offset in valid area */ if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) && (info->cap_offset < sizeof(*info) || info->cap_offset > info->argsz)) { - return -EINVAL; + ret = -EINVAL; + } else if (vfio_get_region_info_cap(info, + VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS)) { + if (info->index < VFIO_USER_MAX_REGIONS && user_fds.recv_fds > 0) { + VFIOUserFDs *saved = vfio_user_getfds(user_fds.recv_fds); + + saved->recv_fds = user_fds.recv_fds; + memcpy(saved->fds, fds, user_fds.recv_fds * sizeof(int)); + vfio_user_free_fds(vbasedev->proxy->region_fds[info->index]); + vbasedev->proxy->region_fds[info->index] = saved; + return 0; + } + } else if (user_fds.recv_fds > 0) { + *fd = fds[0]; + fds[0] = -1; } - return 0; + for (i = 0; i < user_fds.recv_fds; i++) { + if (fds[i] >= 0) { + close(fds[i]); + } + } + return ret; } static int vfio_user_device_io_get_irq_info(VFIODevice *vbasedev, @@ -472,12 +496,64 @@ static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index, /* * Socket-based io_ops */ +static int vfio_user_device_io_setup_sparse_mmaps(VFIORegion *region, + struct vfio_region_info *info, + Error **errp) +{ + struct vfio_info_cap_header *hdr; + struct vfio_region_info_cap_sparse_mmap_fds *sparse_fds; + VFIOUserFDs *user_fds = NULL; + int i, j = 0; + + hdr = vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS); + if (!hdr) { + return vfio_default_setup_sparse_mmaps(region, info, errp); + } + + sparse_fds = container_of(hdr, struct vfio_region_info_cap_sparse_mmap_fds, + header); + + trace_vfio_region_sparse_mmap_header(region->vbasedev->name, + region->nr, sparse_fds->nr_areas); + + if (region->nr < VFIO_USER_MAX_REGIONS) { + user_fds = region->vbasedev->proxy->region_fds[region->nr]; + } + + region->mmaps = g_new0(VFIOMmap, sparse_fds->nr_areas); + + for (i = 0; i < sparse_fds->nr_areas; i++) { + if (sparse_fds->areas[i].size) { + uint64_t end = sparse_fds->areas[i].offset + + sparse_fds->areas[i].size - 1; + int fd = -1; + + if (user_fds && + sparse_fds->areas[i].fd_index < user_fds->recv_fds) { + fd = user_fds->fds[sparse_fds->areas[i].fd_index]; + } + + trace_vfio_region_sparse_mmap_entry(i, sparse_fds->areas[i].offset, + end); + region->mmaps[j].offset = sparse_fds->areas[i].offset; + region->mmaps[j].fd_offset = sparse_fds->areas[i].fd_offset; + region->mmaps[j].size = sparse_fds->areas[i].size; + region->mmaps[j].fd = fd; + j++; + } + } + + region->nr_mmaps = j; + region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap)); + return 0; +} + VFIODeviceIOOps vfio_user_device_io_ops_sock = { .device_feature = vfio_user_device_io_device_feature, .get_region_info = vfio_user_device_io_get_region_info, + .setup_sparse_mmaps = vfio_user_device_io_setup_sparse_mmaps, .get_irq_info = vfio_user_device_io_get_irq_info, .set_irqs = vfio_user_device_io_set_irqs, .region_read = vfio_user_device_io_region_read, .region_write = vfio_user_device_io_region_write, - }; diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index 197aee07bf..a686ffe25e 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -363,6 +363,8 @@ static int vfio_user_recv_one(VFIOUserProxy *proxy, Error **errp) } memcpy(msg->fds->fds, fdp, numfds * sizeof(int)); + } else if (msg->fds != NULL) { + msg->fds->recv_fds = 0; } /* @@ -740,6 +742,10 @@ bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, qemu_mutex_unlock(&proxy->lock); + if (!ok && fds != NULL) { + fds->recv_fds = 0; + } + return ok; } @@ -867,17 +873,30 @@ void vfio_user_send_error(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int error) } /* - * Close FDs erroneously received in an incoming request. + * Close and free received FDs. */ -void vfio_user_putfds(VFIOUserMsg *msg) +void vfio_user_free_fds(VFIOUserFDs *fds) { - VFIOUserFDs *fds = msg->fds; int i; + if (!fds) { + return; + } + for (i = 0; i < fds->recv_fds; i++) { - close(fds->fds[i]); + if (fds->fds[i] >= 0) { + close(fds->fds[i]); + } } g_free(fds); +} + +/* + * Close FDs erroneously received in an incoming request. + */ +void vfio_user_putfds(VFIOUserMsg *msg) +{ + vfio_user_free_fds(msg->fds); msg->fds = NULL; } @@ -967,6 +986,7 @@ void vfio_user_set_handler(VFIODevice *vbasedev, void vfio_user_disconnect(VFIOUserProxy *proxy) { VFIOUserMsg *r1, *r2; + int i; qemu_mutex_lock(&proxy->lock); @@ -1027,6 +1047,10 @@ void vfio_user_disconnect(VFIOUserProxy *proxy) vfio_user_iothread = NULL; } + for (i = 0; i < VFIO_USER_MAX_REGIONS; i++) { + vfio_user_free_fds(proxy->region_fds[i]); + } + g_free(proxy->sockname); g_free(proxy); } -- 2.55.0.1082.g2b9226bbc0-goog
