aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/vfio-user.rst12
-rw-r--r--include/libvfio-user.h19
-rw-r--r--include/vfio-user.h42
-rw-r--r--lib/libvfio-user.c182
-rw-r--r--lib/private.h12
-rw-r--r--test/py/libvfio_user.py99
-rw-r--r--test/py/test_device_get_region_io_fds.py294
7 files changed, 652 insertions, 8 deletions
diff --git a/docs/vfio-user.rst b/docs/vfio-user.rst
index e912fb3..9d03f4d 100644
--- a/docs/vfio-user.rst
+++ b/docs/vfio-user.rst
@@ -322,9 +322,9 @@ usual ``msg_size`` field in the header, not the ``argsz`` field.
In a reply, the server sets ``argsz`` field to the size needed for a full
payload size. This may be less than the requested maximum size. This may be
-larger than the requested maximum size: in that case, the full payload is not
-included in the reply, but the ``argsz`` field in the reply indicates the needed
-size, allowing a client to allocate a larger buffer for holding the reply before
+larger than the requested maximum size: in that case, the payload reply header
+is returned, but the ``argsz`` field in the reply indicates the needed size,
+allowing a client to allocate a larger buffer for holding the reply before
trying again.
In addition, during negotiation (see `Version`_), the client and server may
@@ -1000,7 +1000,7 @@ Reply
* *argsz* is the size of the region IO FD info structure plus the
total size of the sub-region array. Thus, each array entry "i" is at offset
- i * ((argsz - 32) / count). Note that currently this is 40 bytes for both IO
+ i * ((argsz - 16) / count). Note that currently this is 40 bytes for both IO
FD types, but this is not to be relied on. As elsewhere, this indicates the
full reply payload size needed.
* *flags* must be zero
@@ -1016,8 +1016,8 @@ Note that it is the client's responsibility to verify the requested values (for
example, that the requested offset does not exceed the region's bounds).
Each sub-region given in the response has one of two possible structures,
-depending whether *type* is ``VFIO_USER_IO_FD_TYPE_IOEVENTFD`` or
-``VFIO_USER_IO_FD_TYPE_IOREGIONFD``:
+depending whether *type* is ``VFIO_USER_IO_FD_TYPE_IOEVENTFD`` (0) or
+``VFIO_USER_IO_FD_TYPE_IOREGIONFD`` (1):
Sub-Region IO FD info format (ioeventfd)
""""""""""""""""""""""""""""""""""""""""
diff --git a/include/libvfio-user.h b/include/libvfio-user.h
index 148d76e..a4d8105 100644
--- a/include/libvfio-user.h
+++ b/include/libvfio-user.h
@@ -897,6 +897,25 @@ vfu_pci_find_next_capability(vfu_ctx_t *vfu_ctx, bool extended,
bool
vfu_sg_is_mappable(vfu_ctx_t *vfu_ctx, dma_sg_t *sg);
+/*
+ * Creates a new ioeventfd at the given setup memory region with @offset, @size,
+ * @fd, @flags and @datamatch.
+ *
+ * Returns 0 on success and -1 on failure with errno set.
+ *
+ * @vfu_ctx: the libvfio-user context
+ * @region_idx: The index of the memory region to set up the ioeventfd
+ * @fd: the value of the file descriptor
+ * @offset: The offset into the memory region
+ * @size: size of the ioeventfd
+ * @flags: Any flags to set up the ioeventfd
+ * @datamatch: sets the datamatch value
+ */
+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);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/vfio-user.h b/include/vfio-user.h
index b5e8962..7439484 100644
--- a/include/vfio-user.h
+++ b/include/vfio-user.h
@@ -159,6 +159,48 @@ struct vfio_user_irq_info {
uint32_t subindex;
} __attribute__((packed));
+typedef struct vfio_user_region_io_fds_request {
+ uint32_t argsz;
+ uint32_t flags;
+ uint32_t index;
+ uint32_t count;
+} __attribute__((packed)) vfio_user_region_io_fds_request_t;
+
+#define VFIO_USER_IO_FD_TYPE_IOEVENTFD 0
+#define VFIO_USER_IO_FD_TYPE_IOREGIONFD 1
+
+typedef struct vfio_user_sub_region_ioeventfd {
+ uint64_t offset;
+ uint64_t size;
+ uint32_t fd_index;
+ uint32_t type;
+ uint32_t flags;
+ uint32_t padding;
+ uint64_t datamatch;
+} __attribute__((packed)) vfio_user_sub_region_ioeventfd_t;
+
+typedef struct vfio_user_sub_region_ioregionfd {
+ uint64_t offset;
+ uint64_t size;
+ uint32_t fd_index;
+ uint32_t type;
+ uint32_t flags;
+ uint32_t padding;
+ uint64_t user_data;
+} __attribute__((packed)) vfio_user_sub_region_ioregionfd_t;
+
+typedef struct vfio_user_region_io_fds_reply {
+ uint32_t argsz;
+ uint32_t flags;
+ uint32_t index;
+ uint32_t count;
+ union sub_region {
+ struct vfio_user_sub_region_ioeventfd ioeventfd;
+ struct vfio_user_sub_region_ioregionfd ioregionfd;
+ } sub_regions[];
+} __attribute__((packed)) vfio_user_region_io_fds_reply_t;
+
+
/* Analogous to vfio_iommu_type1_dirty_bitmap. */
struct vfio_user_dirty_pages {
uint32_t argsz;
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)
{
diff --git a/test/py/libvfio_user.py b/test/py/libvfio_user.py
index bf3e9cd..1ad5db5 100644
--- a/test/py/libvfio_user.py
+++ b/test/py/libvfio_user.py
@@ -34,6 +34,7 @@
from collections import namedtuple
from types import SimpleNamespace
import ctypes as c
+import array
import errno
import json
import mmap
@@ -170,6 +171,10 @@ VFIO_IOMMU_DIRTY_PAGES_FLAG_START = (1 << 0)
VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP = (1 << 1)
VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP = (1 << 2)
+VFIO_USER_IO_FD_TYPE_IOEVENTFD = 0
+VFIO_USER_IO_FD_TYPE_IOREGIONFD = 1
+
+
# enum vfu_dev_irq_type
VFU_DEV_INTX_IRQ = 0
VFU_DEV_MSI_IRQ = 1
@@ -329,6 +334,55 @@ class vfio_region_sparse_mmap_area(Structure):
("size", c.c_uint64),
]
+class vfio_user_region_io_fds_request(Structure):
+ _pack_ = 1
+ _fields_ = [
+ ("argsz", c.c_uint32),
+ ("flags", c.c_uint32),
+ ("index", c.c_uint32),
+ ("count", c.c_uint32)
+ ]
+
+class vfio_user_sub_region_ioeventfd(Structure):
+ _pack_ = 1
+ _fields_ = [
+ ("offset", c.c_uint64),
+ ("size", c.c_uint64),
+ ("fd_index", c.c_uint32),
+ ("type", c.c_uint32),
+ ("flags", c.c_uint32),
+ ("padding", c.c_uint32),
+ ("datamatch", c.c_uint64)
+ ]
+
+class vfio_user_sub_region_ioregionfd(Structure):
+ _pack_ = 1
+ _fields_ = [
+ ("offset", c.c_uint64),
+ ("size", c.c_uint64),
+ ("fd_index", c.c_uint32),
+ ("type", c.c_uint32),
+ ("flags", c.c_uint32),
+ ("padding", c.c_uint32),
+ ("user_data", c.c_uint64)
+ ]
+
+class vfio_user_sub_region_io_fd(c.Union):
+ _pack_ = 1
+ _fields_ = [
+ ("sub_region_ioeventfd", vfio_user_sub_region_ioeventfd),
+ ("sub_region_ioregionfd", vfio_user_sub_region_ioregionfd)
+ ]
+
+class vfio_user_region_io_fds_reply(Structure):
+ _pack_ = 1
+ _fields_ = [
+ ("argsz", c.c_uint32),
+ ("flags", c.c_uint32),
+ ("index", c.c_uint32),
+ ("count", c.c_uint32)
+ ]
+
class vfio_user_dma_map(Structure):
_pack_ = 1
_fields_ = [
@@ -406,7 +460,7 @@ class dma_sg_t(Structure):
("length", c.c_uint64),
("offset", c.c_uint64),
("writeable", c.c_bool),
- ("le_next", c.c_void_p), # FIXME add struct for LIST_ENTRY
+ ("le_next", c.c_void_p), # FIXME add struct for LIST_ENTRY
("le_prev", c.c_void_p),
]
@@ -455,6 +509,11 @@ lib.vfu_map_sg.argtypes = (c.c_void_p, c.POINTER(dma_sg_t), c.POINTER(iovec_t),
lib.vfu_unmap_sg.argtypes = (c.c_void_p, c.POINTER(dma_sg_t),
c.POINTER(iovec_t), c.c_int)
+lib.vfu_create_ioeventfd.argtypes = (c.c_void_p, c.c_uint32, c.c_int,
+ c.c_size_t, c.c_uint32, c.c_uint32,
+ c.c_uint64)
+
+
def to_byte(val):
"""Cast an int to a byte value."""
return val.to_bytes(1, 'little')
@@ -519,6 +578,40 @@ def msg(ctx, sock, cmd, payload, expect=0, fds=None):
return get_reply(sock, expect=expect)
+def get_reply_fds(sock, expect=0):
+ """Receives a message from a socket and pulls the returned file descriptors
+ out of the message."""
+ fds = array.array("i")
+ data, ancillary, flags, addr = sock.recvmsg(4096,
+ socket.CMSG_LEN(64 * fds.itemsize))
+ (msg_id, cmd, msg_size, msg_flags, errno) = struct.unpack("HHIII",
+ data[0:16])
+ assert errno == expect
+
+ cmsg_level, cmsg_type, packed_fd = ancillary[0] if len(ancillary) != 0 else \
+ (0, 0, [])
+ unpacked_fds = []
+ for i in range(0, len(packed_fd), 4):
+ [unpacked_fd] = struct.unpack_from("i", packed_fd, offset = i)
+ unpacked_fds.append(unpacked_fd)
+ assert len(packed_fd)/4 == len(unpacked_fds)
+ assert (msg_flags & VFIO_USER_F_TYPE_REPLY) != 0
+ return (unpacked_fds, data[16:])
+
+def msg_fds(ctx, sock, cmd, payload, expect=0, fds=None):
+ """Round trip a request and reply to the server. With the server returning
+ new fds"""
+ hdr = vfio_user_header(cmd, size=len(payload))
+
+ if fds:
+ sock.sendmsg([hdr + payload], [(socket.SOL_SOCKET, socket.SCM_RIGHTS,
+ struct.pack("I" * len(fds), *fds))])
+ else:
+ sock.send(hdr + payload)
+
+ vfu_run_ctx(ctx)
+ return get_reply_fds(sock, expect=expect)
+
def get_pci_header(ctx):
ptr = lib.vfu_pci_get_config_space(ctx)
return c.cast(ptr, c.POINTER(vfu_pci_hdr_t)).contents
@@ -761,4 +854,8 @@ def vfu_map_sg(ctx, sg, iovec, cnt=1, flags=0):
def vfu_unmap_sg(ctx, sg, iovec, cnt=1):
return lib.vfu_unmap_sg(ctx, sg, iovec, cnt)
+def vfu_create_ioeventfd(ctx, region_idx, fd, offset, size, flags, datamatch):
+ assert ctx != None
+
+ return lib.vfu_create_ioeventfd(ctx, region_idx, fd, offset, size, flags, datamatch)
# ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: #
diff --git a/test/py/test_device_get_region_io_fds.py b/test/py/test_device_get_region_io_fds.py
new file mode 100644
index 0000000..ab29dbb
--- /dev/null
+++ b/test/py/test_device_get_region_io_fds.py
@@ -0,0 +1,294 @@
+#
+# Copyright (c) 2021 Nutanix Inc. All rights reserved.
+#
+# Authors: Jack Kelly <jack.kelly@nutanix.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Nutanix nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+# DAMAGE.
+#
+
+from libvfio_user import *
+import ctypes as c
+import errno
+import tempfile
+import os
+import struct
+import ctypes
+
+ctx = None
+sock = None
+fds = []
+IOEVENT_SIZE = 8
+
+def test_device_get_region_io_fds_setup():
+ global ctx, sock
+
+ ctx = vfu_create_ctx(flags=LIBVFIO_USER_FLAG_ATTACH_NB)
+ assert ctx != None
+
+ f = tempfile.TemporaryFile()
+ f.truncate(65536)
+
+ mmap_areas = [ (0x2000, 0x1000), (0x4000, 0x2000) ]
+
+ ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_BAR1_REGION_IDX, size=0x8000,
+ flags=(VFU_REGION_FLAG_RW | VFU_REGION_FLAG_MEM),
+ mmap_areas=mmap_areas, fd=f.fileno(), offset=0x8000)
+ assert ret == 0
+
+ f = tempfile.TemporaryFile()
+ f.truncate(65536)
+
+ mmap_areas = [ (0x2000, 0x1000), (0x4000, 0x2000) ]
+
+ ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_BAR2_REGION_IDX, size=0x8000,
+ flags=(VFU_REGION_FLAG_RW | VFU_REGION_FLAG_MEM),
+ mmap_areas=mmap_areas, fd=f.fileno(), offset=0x8000)
+ assert ret == 0
+
+ ret = vfu_realize_ctx(ctx)
+ assert ret == 0
+
+ sock = connect_client(ctx)
+ for i in range(0,6):
+ tmp = eventfd(0,0)
+ fds.append(tmp)
+ assert vfu_create_ioeventfd(ctx, VFU_PCI_DEV_BAR2_REGION_IDX, tmp,
+ i * IOEVENT_SIZE, IOEVENT_SIZE, 0, 0) != -1
+
+def test_device_get_region_io_fds_bad_flags():
+
+ payload = vfio_user_region_io_fds_request(
+ argsz = len(vfio_user_region_io_fds_reply()) +
+ len(vfio_user_sub_region_ioeventfd()) * 5, flags = 1,
+ index = VFU_PCI_DEV_BAR2_REGION_IDX, count = 0)
+
+ msg(ctx, sock, VFIO_USER_DEVICE_GET_REGION_IO_FDS, payload,
+ expect=errno.EINVAL)
+
+def test_device_get_region_io_fds_bad_count():
+
+ payload = vfio_user_region_io_fds_request(
+ argsz = len(vfio_user_region_io_fds_reply()) +
+ len(vfio_user_sub_region_ioeventfd()) * 5, flags = 0,
+ index = VFU_PCI_DEV_BAR2_REGION_IDX, count = 1)
+
+ msg(ctx, sock, VFIO_USER_DEVICE_GET_REGION_IO_FDS, payload,
+ expect=errno.EINVAL)
+
+def test_device_get_region_io_fds_buffer_too_small():
+
+ payload = vfio_user_region_io_fds_request(
+ argsz = len(vfio_user_region_io_fds_reply()) - 1, flags = 0,
+ index = VFU_PCI_DEV_BAR2_REGION_IDX, count = 1)
+
+ msg(ctx, sock, VFIO_USER_DEVICE_GET_REGION_IO_FDS, payload,
+ expect=errno.EINVAL)
+
+def test_device_get_region_io_fds_buffer_too_large():
+
+ payload = vfio_user_region_io_fds_request(argsz = SERVER_MAX_DATA_XFER_SIZE
+ + 1, flags = 0,
+ index = VFU_PCI_DEV_BAR2_REGION_IDX,
+ count = 1)
+
+ msg(ctx, sock, VFIO_USER_DEVICE_GET_REGION_IO_FDS, payload, expect =
+ errno.EINVAL)
+
+def test_device_get_region_io_fds_no_regions():
+
+ payload = vfio_user_region_io_fds_request(argsz = 512, flags = 0,
+ index = VFU_PCI_DEV_BAR1_REGION_IDX, count = 0)
+
+ ret = msg(ctx, sock, VFIO_USER_DEVICE_GET_REGION_IO_FDS, payload, expect=0)
+
+ reply, ret = vfio_user_region_io_fds_reply.pop_from_buffer(ret)
+
+ assert reply.argsz == len(vfio_user_region_io_fds_reply())
+ assert reply.count == 0
+ assert reply.flags == 0
+ assert reply.index == VFU_PCI_DEV_BAR1_REGION_IDX
+
+
+def test_device_get_region_io_fds_no_regions_setup():
+
+ payload = vfio_user_region_io_fds_request(argsz = 512, flags = 0,
+ index = VFU_PCI_DEV_BAR3_REGION_IDX, count = 0)
+
+ ret = msg(ctx, sock, VFIO_USER_DEVICE_GET_REGION_IO_FDS, payload,
+ expect=errno.EINVAL)
+
+def test_device_get_region_io_fds_region_out_of_range():
+
+ payload = vfio_user_region_io_fds_request(argsz = 512, flags = 0,
+ index = 512, count = 0)
+
+ msg(ctx, sock, VFIO_USER_DEVICE_GET_REGION_IO_FDS, payload, expect =
+ errno.EINVAL)
+
+def test_device_get_region_io_fds_fds_read_write():
+
+ payload = vfio_user_region_io_fds_request(
+ argsz = len(vfio_user_region_io_fds_reply()) +
+ len(vfio_user_sub_region_ioeventfd()) * 10, flags = 0,
+ index = VFU_PCI_DEV_BAR2_REGION_IDX, count = 0)
+
+ newfds, ret = msg_fds(ctx, sock, VFIO_USER_DEVICE_GET_REGION_IO_FDS,
+ payload, expect=0)
+
+ assert len(newfds) == 6
+ _, ret = vfio_user_region_io_fds_reply.pop_from_buffer(ret)
+ _, ret = vfio_user_sub_region_ioeventfd.pop_from_buffer(ret)
+
+ # Simulating a VM triggering an ioeventfd and the server waking up
+
+ # Client
+ for i in range(0, len(newfds)):
+ os.write(newfds[i], c.c_ulonglong(10))
+
+ # Server
+ for i in range(0, len(newfds)):
+ out = os.read(newfds[i], IOEVENT_SIZE)
+ [out] = struct.unpack("@Q",out)
+ assert out == 10
+
+ for i in newfds:
+ os.close(i)
+
+def test_device_get_region_io_fds_full():
+
+ payload = vfio_user_region_io_fds_request(
+ argsz = len(vfio_user_region_io_fds_reply()) +
+ len(vfio_user_sub_region_ioeventfd()) * 6, flags = 0,
+ index = VFU_PCI_DEV_BAR2_REGION_IDX, count = 0)
+
+ newfds, ret = msg_fds(ctx, sock, VFIO_USER_DEVICE_GET_REGION_IO_FDS,
+ payload, expect=0)
+
+ reply, ret = vfio_user_region_io_fds_reply.pop_from_buffer(ret)
+ assert len(newfds) == reply.count
+ ioevents = []
+ for i in range(0, reply.count):
+ ioevent, ret = vfio_user_sub_region_ioeventfd.pop_from_buffer(ret)
+ ioevents.append(ioevent)
+ os.write(newfds[ioevent.fd_index], c.c_ulonglong(1))
+
+ for i in range(0, reply.count):
+ out = os.read(newfds[ioevents[i].fd_index], ioevent.size)
+ [out] = struct.unpack("@Q",out)
+ assert out == 1
+ assert ioevents[i].size == IOEVENT_SIZE
+ assert ioevents[i].offset == 40 - (IOEVENT_SIZE * i)
+ assert ioevents[i].type == VFIO_USER_IO_FD_TYPE_IOEVENTFD
+
+ for i in newfds:
+ os.close(i)
+
+def test_device_get_region_io_fds_fds_read_write_nothing():
+
+ payload = vfio_user_region_io_fds_request(
+ argsz = len(vfio_user_region_io_fds_reply()), flags = 0,
+ index = VFU_PCI_DEV_BAR2_REGION_IDX, count = 0)
+
+ newfds, ret = msg_fds(ctx, sock, VFIO_USER_DEVICE_GET_REGION_IO_FDS,
+ payload, expect=0)
+
+ assert len(newfds) == 0
+ reply, _ = vfio_user_region_io_fds_request.pop_from_buffer(ret)
+ assert reply.argsz == len(vfio_user_region_io_fds_reply()) + \
+ len(vfio_user_sub_region_ioeventfd()) * 6
+
+def test_device_get_region_io_fds_fds_read_write_dupe_fd():
+ """ Test here to show that we can return mutliple sub regions with the same
+ fd_index. fd_index points to the list of fds returned from the socket as
+ returned by msg_fds. """
+
+ t = eventfd(0,0)
+ assert vfu_create_ioeventfd(ctx, VFU_PCI_DEV_BAR2_REGION_IDX, t, 6 *
+ IOEVENT_SIZE, IOEVENT_SIZE, 0, 0) != -1
+ assert vfu_create_ioeventfd(ctx, VFU_PCI_DEV_BAR2_REGION_IDX, t, 7 *
+ IOEVENT_SIZE, IOEVENT_SIZE, 0, 0) != -1
+
+ payload = vfio_user_region_io_fds_request(
+ argsz = len(vfio_user_region_io_fds_reply()) +
+ len(vfio_user_sub_region_ioeventfd()) * 8, flags = 0,
+ index = VFU_PCI_DEV_BAR2_REGION_IDX, count = 0)
+
+ newfds, ret = msg_fds(ctx, sock, VFIO_USER_DEVICE_GET_REGION_IO_FDS,
+ payload, expect=0)
+ reply, ret = vfio_user_region_io_fds_reply.pop_from_buffer(ret)
+ assert len(newfds) == 7
+ assert reply.count == 8
+ assert reply.argsz == len(vfio_user_region_io_fds_reply()) + \
+ len(vfio_user_sub_region_ioeventfd()) * 8
+
+ ioevents = []
+ for i in range(0, reply.count):
+ ioevent, ret = vfio_user_sub_region_ioeventfd.pop_from_buffer(ret)
+ ioevents.append(ioevent)
+
+ for i in range(2, 8):
+ os.write(newfds[ioevents[i].fd_index], c.c_ulonglong(1))
+
+ for i in range(2, 8):
+ out = os.read(newfds[ioevents[i].fd_index], ioevent.size)
+ [out] = struct.unpack("@Q",out)
+ assert out == 1
+ assert ioevents[i].size == IOEVENT_SIZE
+ assert ioevents[i].offset == 56 - (IOEVENT_SIZE * i)
+ assert ioevents[i].type == VFIO_USER_IO_FD_TYPE_IOEVENTFD
+
+ assert ioevents[0].fd_index == ioevents[1].fd_index
+ assert ioevents[0].offset != ioevents[1].offset
+
+ os.write(newfds[ioevents[0].fd_index], c.c_ulonglong(1))
+
+ out = os.read(newfds[ioevents[1].fd_index], ioevent.size)
+ [out] = struct.unpack("@Q",out)
+ assert out == 1
+
+ os.write(newfds[ioevents[1].fd_index], c.c_ulonglong(1))
+
+ out = os.read(newfds[ioevents[0].fd_index], ioevent.size)
+ [out] = struct.unpack("@Q",out)
+ assert out == 1
+
+ os.write(newfds[ioevents[0].fd_index], c.c_ulonglong(1))
+ out = os.read(newfds[ioevents[1].fd_index], ioevent.size)
+ [out] = struct.unpack("@Q",out)
+ assert out == 1
+
+ for i in newfds:
+ os.close(i)
+
+def test_device_get_region_io_fds_ioeventfd_invalid_size():
+
+ t = eventfd(0,0)
+ assert vfu_create_ioeventfd(ctx, VFU_PCI_DEV_BAR2_REGION_IDX, t, 0x8000
+ -2048, 4096, 0, 0) == -1
+ os.close(t)
+
+def test_device_get_region_info_cleanup():
+ for i in fds:
+ os.close(i)
+ vfu_destroy_ctx(ctx)