diff options
author | William Henderson <william.henderson@nutanix.com> | 2023-07-25 14:06:42 +0000 |
---|---|---|
committer | John Levon <john.levon@nutanix.com> | 2023-09-15 13:06:15 +0100 |
commit | 1e212acd051c732a123eb7d1fd11c486a376bd66 (patch) | |
tree | e701f88681d91f4b74347a40a8d3fe4ce9fb75b5 | |
parent | 2287a79a2ddb9d47bead774d2aaf014f6034a44e (diff) | |
download | libvfio-user-1e212acd051c732a123eb7d1fd11c486a376bd66.zip libvfio-user-1e212acd051c732a123eb7d1fd11c486a376bd66.tar.gz libvfio-user-1e212acd051c732a123eb7d1fd11c486a376bd66.tar.bz2 |
test: update existing tests
Signed-off-by: William Henderson <william.henderson@nutanix.com>
-rw-r--r-- | test/mocks.c | 19 | ||||
-rw-r--r-- | test/py/libvfio_user.py | 85 | ||||
-rw-r--r-- | test/py/meson.build | 1 | ||||
-rw-r--r-- | test/py/test_device_get_region_info.py | 46 | ||||
-rw-r--r-- | test/py/test_device_get_region_info_zero_size.py | 41 | ||||
-rw-r--r-- | test/py/test_dirty_pages.py | 498 | ||||
-rw-r--r-- | test/py/test_dma_unmap.py | 20 | ||||
-rw-r--r-- | test/py/test_migration.py | 139 | ||||
-rw-r--r-- | test/py/test_quiesce.py | 32 | ||||
-rw-r--r-- | test/py/test_request_errors.py | 53 | ||||
-rw-r--r-- | test/py/test_setup_region.py | 24 | ||||
-rw-r--r-- | test/unit-tests.c | 285 |
12 files changed, 41 insertions, 1202 deletions
diff --git a/test/mocks.c b/test/mocks.c index 2ae14b4..ce9e5ca 100644 --- a/test/mocks.c +++ b/test/mocks.c @@ -200,23 +200,6 @@ should_exec_command(vfu_ctx_t *vfu_ctx, uint16_t cmd) } ssize_t -migration_region_access_registers(vfu_ctx_t *vfu_ctx, char *buf, size_t count, - loff_t pos, bool is_write) -{ - if (!is_patched("migration_region_access_registers")) { - return __real_migration_region_access_registers(vfu_ctx, buf, count, - pos, is_write); - } - check_expected(vfu_ctx); - check_expected(buf); - check_expected(count); - check_expected(pos); - check_expected(is_write); - errno = mock(); - return mock(); -} - -ssize_t handle_device_state(vfu_ctx_t *vfu_ctx, struct migration *migr, uint32_t device_state, bool notify) { @@ -232,7 +215,7 @@ handle_device_state(vfu_ctx_t *vfu_ctx, struct migration *migr, } void -migr_state_transition(struct migration *migr, enum migr_iter_state state) +migr_state_transition(struct migration *migr, enum vfio_device_mig_state state) { if (!is_patched("migr_state_transition")) { __real_migr_state_transition(migr, state); diff --git a/test/py/libvfio_user.py b/test/py/libvfio_user.py index 8848dbf..be58ae2 100644 --- a/test/py/libvfio_user.py +++ b/test/py/libvfio_user.py @@ -42,7 +42,6 @@ import socket import struct import syslog import copy -import tempfile import sys from resource import getpagesize from math import log2 @@ -171,7 +170,11 @@ VFIO_USER_DMA_READ = 11 VFIO_USER_DMA_WRITE = 12 VFIO_USER_DEVICE_RESET = 13 VFIO_USER_DIRTY_PAGES = 14 -VFIO_USER_MAX = 15 +VFIO_USER_REGION_WRITE_MULTI = 15 +VFIO_USER_DEVICE_FEATURE = 16 +VFIO_USER_MIG_DATA_READ = 17 +VFIO_USER_MIG_DATA_WRITE = 18 +VFIO_USER_MAX = 19 VFIO_USER_F_TYPE_COMMAND = 0 VFIO_USER_F_TYPE_REPLY = 1 @@ -187,8 +190,7 @@ VFU_PCI_DEV_BAR5_REGION_IDX = 5 VFU_PCI_DEV_ROM_REGION_IDX = 6 VFU_PCI_DEV_CFG_REGION_IDX = 7 VFU_PCI_DEV_VGA_REGION_IDX = 8 -VFU_PCI_DEV_MIGR_REGION_IDX = 9 -VFU_PCI_DEV_NUM_REGIONS = 10 +VFU_PCI_DEV_NUM_REGIONS = 9 VFU_REGION_FLAG_READ = 1 VFU_REGION_FLAG_WRITE = 2 @@ -233,7 +235,7 @@ VFU_CAP_FLAG_EXTENDED = (1 << 0) VFU_CAP_FLAG_CALLBACK = (1 << 1) VFU_CAP_FLAG_READONLY = (1 << 2) -VFU_MIGR_CALLBACKS_VERS = 1 +VFU_MIGR_CALLBACKS_VERS = 2 SOCK_PATH = b"/tmp/vfio-user.sock.%d" % os.getpid() @@ -534,24 +536,16 @@ class vfio_user_bitmap_range(Structure): transition_cb_t = c.CFUNCTYPE(c.c_int, c.c_void_p, c.c_int, use_errno=True) -get_pending_bytes_cb_t = c.CFUNCTYPE(c.c_uint64, c.c_void_p) -prepare_data_cb_t = c.CFUNCTYPE(c.c_void_p, c.POINTER(c.c_uint64), - c.POINTER(c.c_uint64)) -read_data_cb_t = c.CFUNCTYPE(c.c_ssize_t, c.c_void_p, c.c_void_p, - c.c_uint64, c.c_uint64) -write_data_cb_t = c.CFUNCTYPE(c.c_ssize_t, c.c_void_p, c.c_uint64) -data_written_cb_t = c.CFUNCTYPE(c.c_int, c.c_void_p, c.c_uint64) +read_data_cb_t = c.CFUNCTYPE(c.c_ssize_t, c.c_void_p, c.c_void_p, c.c_uint64) +write_data_cb_t = c.CFUNCTYPE(c.c_ssize_t, c.c_void_p, c.c_void_p, c.c_uint64) class vfu_migration_callbacks_t(Structure): _fields_ = [ ("version", c.c_int), ("transition", transition_cb_t), - ("get_pending_bytes", get_pending_bytes_cb_t), - ("prepare_data", prepare_data_cb_t), ("read_data", read_data_cb_t), ("write_data", write_data_cb_t), - ("data_written", data_written_cb_t), ] @@ -570,17 +564,6 @@ class dma_sg_t(Structure): hex(self.offset), self.writeable) -class vfio_user_migration_info(Structure): - _pack_ = 1 - _fields_ = [ - ("device_state", c.c_uint32), - ("reserved", c.c_uint32), - ("pending_bytes", c.c_uint64), - ("data_offset", c.c_uint64), - ("data_size", c.c_uint64), - ] - - # # Util functions # @@ -623,8 +606,8 @@ vfu_dma_unregister_cb_t = c.CFUNCTYPE(None, c.c_void_p, use_errno=True) lib.vfu_setup_device_dma.argtypes = (c.c_void_p, vfu_dma_register_cb_t, vfu_dma_unregister_cb_t) -lib.vfu_setup_device_migration_callbacks.argtypes = (c.c_void_p, - c.POINTER(vfu_migration_callbacks_t), c.c_uint64) +lib.vfu_setup_device_migration_callbacks.argtypes = (c.c_void_p, c.c_uint64, + c.POINTER(vfu_migration_callbacks_t)) lib.dma_sg_size.restype = (c.c_size_t) lib.vfu_addr_to_sgl.argtypes = (c.c_void_p, c.c_void_p, c.c_size_t, c.POINTER(dma_sg_t), c.c_size_t, c.c_int) @@ -929,18 +912,6 @@ def prepare_ctx_for_dma(dma_register=__dma_register, ret = vfu_setup_device_reset_cb(ctx, reset) assert ret == 0 - f = tempfile.TemporaryFile() - migr_region_size = 2 << PAGE_SHIFT - f.truncate(migr_region_size) - - mmap_areas = [(PAGE_SIZE, PAGE_SIZE)] - - ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_MIGR_REGION_IDX, - size=migr_region_size, - flags=VFU_REGION_FLAG_RW, mmap_areas=mmap_areas, - fd=f.fileno()) - assert ret == 0 - if migration_callbacks: ret = vfu_setup_device_migration_callbacks(ctx) assert ret == 0 @@ -1136,24 +1107,6 @@ def __migr_trans_cb(ctx, state): return migr_trans_cb(ctx, state) -def migr_get_pending_bytes_cb(ctx): - pass - - -@get_pending_bytes_cb_t -def __migr_get_pending_bytes_cb(ctx): - return migr_get_pending_bytes_cb(ctx) - - -def migr_prepare_data_cb(ctx, offset, size): - pass - - -@prepare_data_cb_t -def __migr_prepare_data_cb(ctx, offset, size): - return migr_prepare_data_cb(ctx, offset, size) - - def migr_read_data_cb(ctx, buf, count, offset): pass @@ -1172,29 +1125,17 @@ def __migr_write_data_cb(ctx, buf, count, offset): return migr_write_data_cb(ctx, buf, count, offset) -def migr_data_written_cb(ctx, count): - pass - - -@data_written_cb_t -def __migr_data_written_cb(ctx, count): - return migr_data_written_cb(ctx, count) - - -def vfu_setup_device_migration_callbacks(ctx, cbs=None, offset=PAGE_SIZE): +def vfu_setup_device_migration_callbacks(ctx, flags=0, cbs=None): assert ctx is not None if not cbs: cbs = vfu_migration_callbacks_t() cbs.version = VFU_MIGR_CALLBACKS_VERS cbs.transition = __migr_trans_cb - cbs.get_pending_bytes = __migr_get_pending_bytes_cb - cbs.prepare_data = __migr_prepare_data_cb cbs.read_data = __migr_read_data_cb cbs.write_data = __migr_write_data_cb - cbs.data_written = __migr_data_written_cb - return lib.vfu_setup_device_migration_callbacks(ctx, cbs, offset) + return lib.vfu_setup_device_migration_callbacks(ctx, flags, cbs) def dma_sg_size(): diff --git a/test/py/meson.build b/test/py/meson.build index 0ea9f08..4b3e533 100644 --- a/test/py/meson.build +++ b/test/py/meson.build @@ -33,7 +33,6 @@ python_tests = [ 'test_device_get_region_info_zero_size.py', 'test_device_get_region_io_fds.py', 'test_device_set_irqs.py', - 'test_dirty_pages.py', 'test_dma_map.py', 'test_dma_unmap.py', 'test_irq_trigger.py', diff --git a/test/py/test_device_get_region_info.py b/test/py/test_device_get_region_info.py index 62d6740..9e2ffbb 100644 --- a/test/py/test_device_get_region_info.py +++ b/test/py/test_device_get_region_info.py @@ -78,14 +78,6 @@ def test_device_get_region_info_setup(): mmap_areas=mmap_areas, fd=f.fileno(), offset=0x0) assert ret == 0 - f = tempfile.TemporaryFile() - f.truncate(migr_region_size) - - ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_MIGR_REGION_IDX, - size=migr_region_size, flags=VFU_REGION_FLAG_RW, - mmap_areas=migr_mmap_areas, fd=f.fileno()) - assert ret == 0 - ret = vfu_realize_ctx(ctx) assert ret == 0 @@ -206,44 +198,6 @@ def test_device_get_region_info_caps(): disconnect_client(ctx, sock) -def test_device_get_region_info_migr(): - global sock - - sock = connect_client(ctx) - - payload = vfio_region_info(argsz=80, flags=0, - index=VFU_PCI_DEV_MIGR_REGION_IDX, cap_offset=0, - size=0, offset=0) - payload = bytes(payload) + b'\0' * (80 - 32) - - result = msg(ctx, sock, VFIO_USER_DEVICE_GET_REGION_INFO, payload) - - info, result = vfio_region_info.pop_from_buffer(result) - mcap, result = vfio_region_info_cap_type.pop_from_buffer(result) - cap, result = vfio_region_info_cap_sparse_mmap.pop_from_buffer(result) - area, result = vfio_region_sparse_mmap_area.pop_from_buffer(result) - - assert info.argsz == 80 - assert info.cap_offset == 32 - - assert mcap.id == VFIO_REGION_INFO_CAP_TYPE - assert mcap.version == 1 - assert mcap.next == 48 - assert mcap.type == VFIO_REGION_TYPE_MIGRATION - assert mcap.subtype == VFIO_REGION_SUBTYPE_MIGRATION - - assert cap.id == VFIO_REGION_INFO_CAP_SPARSE_MMAP - assert cap.version == 1 - assert cap.next == 0 - assert cap.nr_areas == len(migr_mmap_areas) == 1 - - assert area.offset == migr_mmap_areas[0][0] - assert area.size == migr_mmap_areas[0][1] - - # skip reading the SCM_RIGHTS - disconnect_client(ctx, sock) - - def test_device_get_region_info_cleanup(): vfu_destroy_ctx(ctx) diff --git a/test/py/test_device_get_region_info_zero_size.py b/test/py/test_device_get_region_info_zero_size.py index 42a6ae0..f792756 100644 --- a/test/py/test_device_get_region_info_zero_size.py +++ b/test/py/test_device_get_region_info_zero_size.py @@ -52,27 +52,26 @@ def test_device_get_region_info_zero_sized_region(): global sock - for index in [VFU_PCI_DEV_BAR1_REGION_IDX, VFU_PCI_DEV_MIGR_REGION_IDX]: - payload = vfio_region_info(argsz=argsz, flags=0, - index=index, cap_offset=0, - size=0, offset=0) - - hdr = vfio_user_header(VFIO_USER_DEVICE_GET_REGION_INFO, - size=len(payload)) - sock.send(hdr + payload) - vfu_run_ctx(ctx) - result = get_reply(sock) - - assert len(result) == argsz - - info, _ = vfio_region_info.pop_from_buffer(result) - - assert info.argsz == argsz - assert info.flags == 0 - assert info.index == index - assert info.cap_offset == 0 - assert info.size == 0 - assert info.offset == 0 + payload = vfio_region_info(argsz=argsz, flags=0, + index=VFU_PCI_DEV_BAR1_REGION_IDX, cap_offset=0, + size=0, offset=0) + + hdr = vfio_user_header(VFIO_USER_DEVICE_GET_REGION_INFO, + size=len(payload)) + sock.send(hdr + payload) + vfu_run_ctx(ctx) + result = get_reply(sock) + + assert len(result) == argsz + + info, _ = vfio_region_info.pop_from_buffer(result) + + assert info.argsz == argsz + assert info.flags == 0 + assert info.index == VFU_PCI_DEV_BAR1_REGION_IDX + assert info.cap_offset == 0 + assert info.size == 0 + assert info.offset == 0 vfu_destroy_ctx(ctx) diff --git a/test/py/test_dirty_pages.py b/test/py/test_dirty_pages.py deleted file mode 100644 index b3d4e34..0000000 --- a/test/py/test_dirty_pages.py +++ /dev/null @@ -1,498 +0,0 @@ -# -# Copyright (c) 2021 Nutanix Inc. All rights reserved. -# -# Authors: John Levon <john.levon@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 -# SERVICESLOSS 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 mmap -import tempfile - -ctx = None -quiesce_errno = 0 - - -@vfu_dma_register_cb_t -def dma_register(ctx, info): - return 0 - - -@vfu_dma_unregister_cb_t -def dma_unregister(ctx, info): - return 0 - - -@vfu_device_quiesce_cb_t -def quiesce_cb(ctx): - if quiesce_errno: - c.set_errno(errno.EBUSY) - return -1 - return 0 - - -def test_dirty_pages_setup(): - global ctx, sock - - ctx = vfu_create_ctx(flags=LIBVFIO_USER_FLAG_ATTACH_NB) - assert ctx is not None - - ret = vfu_pci_init(ctx) - assert ret == 0 - - vfu_setup_device_quiesce_cb(ctx, quiesce_cb=quiesce_cb) - - ret = vfu_setup_device_dma(ctx, dma_register, dma_unregister) - assert ret == 0 - - f = tempfile.TemporaryFile() - f.truncate(2 << PAGE_SHIFT) - - mmap_areas = [(PAGE_SIZE, PAGE_SIZE)] - - ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_MIGR_REGION_IDX, - size=2 << PAGE_SHIFT, flags=VFU_REGION_FLAG_RW, - mmap_areas=mmap_areas, fd=f.fileno()) - assert ret == 0 - - ret = vfu_realize_ctx(ctx) - assert ret == 0 - - sock = connect_client(ctx) - - f = tempfile.TemporaryFile() - f.truncate(0x10 << PAGE_SHIFT) - - payload = vfio_user_dma_map(argsz=len(vfio_user_dma_map()), - flags=(VFIO_USER_F_DMA_REGION_READ | VFIO_USER_F_DMA_REGION_WRITE), - offset=0, addr=0x10 << PAGE_SHIFT, size=0x20 << PAGE_SHIFT) - - msg(ctx, sock, VFIO_USER_DMA_MAP, payload, fds=[f.fileno()]) - - payload = vfio_user_dma_map(argsz=len(vfio_user_dma_map()), - flags=(VFIO_USER_F_DMA_REGION_READ | VFIO_USER_F_DMA_REGION_WRITE), - offset=0, addr=0x40 << PAGE_SHIFT, size=0x10 << PAGE_SHIFT) - - msg(ctx, sock, VFIO_USER_DMA_MAP, payload) - - -def test_dirty_pages_short_write(): - payload = struct.pack("I", 8) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, - expect=errno.EINVAL) - - -def test_dirty_pages_bad_argsz(): - payload = vfio_user_dirty_pages(argsz=4, - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_START) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, - expect=errno.EINVAL) - - -def test_dirty_pages_start_no_migration(): - payload = vfio_user_dirty_pages(argsz=len(vfio_user_dirty_pages()), - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_START) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, - expect=errno.ENOTSUP) - - -def test_setup_migr_region(): - ret = vfu_setup_device_migration_callbacks(ctx, offset=PAGE_SIZE) - assert ret == 0 - - -def test_dirty_pages_start_bad_flags(): - # - # This is a little cheeky, after vfu_realize_ctx(), but it works at the - # moment. - # - payload = vfio_user_dirty_pages(argsz=len(vfio_user_dirty_pages()), - flags=(VFIO_IOMMU_DIRTY_PAGES_FLAG_START | - VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP)) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, - expect=errno.EINVAL) - - payload = vfio_user_dirty_pages(argsz=len(vfio_user_dirty_pages()), - flags=(VFIO_IOMMU_DIRTY_PAGES_FLAG_START | - VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP)) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, - expect=errno.EINVAL) - - -def start_logging(): - payload = vfio_user_dirty_pages(argsz=len(vfio_user_dirty_pages()), - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_START) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload) - - -def test_dirty_pages_start(): - start_logging() - # should be idempotent - start_logging() - - -def test_dirty_pages_get_short_read(): - payload = vfio_user_dirty_pages(argsz=len(vfio_user_dirty_pages()), - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, - expect=errno.EINVAL) - - -# -# This should in fact work; update when it does. -# -def test_dirty_pages_get_sub_range(): - argsz = len(vfio_user_dirty_pages()) + len(vfio_user_bitmap_range()) + 8 - dirty_pages = vfio_user_dirty_pages(argsz=argsz, - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP) - bitmap = vfio_user_bitmap(pgsize=PAGE_SIZE, size=8) - br = vfio_user_bitmap_range(iova=0x11 << PAGE_SHIFT, size=PAGE_SIZE, - bitmap=bitmap) - - payload = bytes(dirty_pages) + bytes(br) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, - expect=errno.ENOTSUP) - - -def test_dirty_pages_get_bad_page_size(): - argsz = len(vfio_user_dirty_pages()) + len(vfio_user_bitmap_range()) + 8 - dirty_pages = vfio_user_dirty_pages(argsz=argsz, - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP) - bitmap = vfio_user_bitmap(pgsize=2 << PAGE_SHIFT, size=8) - br = vfio_user_bitmap_range(iova=0x10 << PAGE_SHIFT, - size=0x10 << PAGE_SHIFT, bitmap=bitmap) - - payload = bytes(dirty_pages) + bytes(br) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, - expect=errno.EINVAL) - - -def test_dirty_pages_get_bad_bitmap_size(): - argsz = len(vfio_user_dirty_pages()) + len(vfio_user_bitmap_range()) + 8 - dirty_pages = vfio_user_dirty_pages(argsz=argsz, - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP) - bitmap = vfio_user_bitmap(pgsize=PAGE_SIZE, size=1) - br = vfio_user_bitmap_range(iova=0x10 << PAGE_SHIFT, - size=0x10 << PAGE_SHIFT, bitmap=bitmap) - - payload = bytes(dirty_pages) + bytes(br) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, - expect=errno.EINVAL) - - -def test_dirty_pages_get_bad_argsz(): - dirty_pages = vfio_user_dirty_pages(argsz=SERVER_MAX_DATA_XFER_SIZE + 8, - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP) - bitmap = vfio_user_bitmap(pgsize=PAGE_SIZE, - size=SERVER_MAX_DATA_XFER_SIZE + 8) - br = vfio_user_bitmap_range(iova=0x10 << PAGE_SHIFT, - size=0x10 << PAGE_SHIFT, bitmap=bitmap) - - payload = bytes(dirty_pages) + bytes(br) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, - expect=errno.EINVAL) - - -def test_dirty_pages_get_short_reply(): - dirty_pages = vfio_user_dirty_pages(argsz=len(vfio_user_dirty_pages()), - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP) - bitmap = vfio_user_bitmap(pgsize=PAGE_SIZE, size=8) - br = vfio_user_bitmap_range(iova=0x10 << PAGE_SHIFT, - size=0x10 << PAGE_SHIFT, bitmap=bitmap) - - payload = bytes(dirty_pages) + bytes(br) - - result = msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload) - - assert len(result) == len(vfio_user_dirty_pages()) - - dirty_pages, _ = vfio_user_dirty_pages.pop_from_buffer(result) - - argsz = len(vfio_user_dirty_pages()) + len(vfio_user_bitmap_range()) + 8 - - assert dirty_pages.argsz == argsz - assert dirty_pages.flags == VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP - - -def test_get_dirty_page_bitmap_unmapped(): - argsz = len(vfio_user_dirty_pages()) + len(vfio_user_bitmap_range()) + 8 - - dirty_pages = vfio_user_dirty_pages(argsz=argsz, - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP) - bitmap = vfio_user_bitmap(pgsize=PAGE_SIZE, size=8) - br = vfio_user_bitmap_range(iova=0x40 << PAGE_SHIFT, - size=0x10 << PAGE_SHIFT, bitmap=bitmap) - - payload = bytes(dirty_pages) + bytes(br) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, - expect=errno.EINVAL) - - -def test_dirty_pages_get_unmodified(): - argsz = len(vfio_user_dirty_pages()) + len(vfio_user_bitmap_range()) + 8 - - dirty_pages = vfio_user_dirty_pages(argsz=argsz, - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP) - bitmap = vfio_user_bitmap(pgsize=PAGE_SIZE, size=8) - br = vfio_user_bitmap_range(iova=0x10 << PAGE_SHIFT, - size=0x10 << PAGE_SHIFT, bitmap=bitmap) - - payload = bytes(dirty_pages) + bytes(br) - - result = msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload) - - assert len(result) == argsz - - dirty_pages, result = vfio_user_dirty_pages.pop_from_buffer(result) - - assert dirty_pages.argsz == argsz - assert dirty_pages.flags == VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP - - br, result = vfio_user_bitmap_range.pop_from_buffer(result) - - assert br.iova == 0x10 << PAGE_SHIFT - assert br.size == 0x10 << PAGE_SHIFT - - assert br.bitmap.pgsize == PAGE_SIZE - assert br.bitmap.size == 8 - - -def get_dirty_page_bitmap(): - argsz = len(vfio_user_dirty_pages()) + len(vfio_user_bitmap_range()) + 8 - - dirty_pages = vfio_user_dirty_pages(argsz=argsz, - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP) - bitmap = vfio_user_bitmap(pgsize=PAGE_SIZE, size=8) - br = vfio_user_bitmap_range(iova=0x10 << PAGE_SHIFT, - size=0x10 << PAGE_SHIFT, bitmap=bitmap) - - payload = bytes(dirty_pages) + bytes(br) - - result = msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload) - - _, result = vfio_user_dirty_pages.pop_from_buffer(result) - _, result = vfio_user_bitmap_range.pop_from_buffer(result) - - assert len(result) == 8 - - return struct.unpack("Q", result)[0] - - -sg3 = None -iovec3 = None - - -def write_to_page(ctx, page, nr_pages, get_bitmap=True): - """Simulate a write to the given address and size.""" - ret, sg = vfu_addr_to_sgl(ctx, dma_addr=page << PAGE_SHIFT, - length=nr_pages << PAGE_SHIFT) - assert ret == 1 - iovec = iovec_t() - ret = vfu_sgl_get(ctx, sg, iovec) - assert ret == 0 - vfu_sgl_put(ctx, sg, iovec) - if get_bitmap: - return get_dirty_page_bitmap() - return None - - -def test_dirty_pages_get_modified(): - ret, sg1 = vfu_addr_to_sgl(ctx, dma_addr=0x10 << PAGE_SHIFT, - length=PAGE_SIZE) - assert ret == 1 - iovec1 = iovec_t() - ret = vfu_sgl_get(ctx, sg1, iovec1) - assert ret == 0 - - # read only - ret, sg2 = vfu_addr_to_sgl(ctx, dma_addr=0x11 << PAGE_SHIFT, - length=PAGE_SIZE, prot=mmap.PROT_READ) - assert ret == 1 - iovec2 = iovec_t() - ret = vfu_sgl_get(ctx, sg2, iovec2) - assert ret == 0 - - # simple single bitmap entry map - ret, sg3 = vfu_addr_to_sgl(ctx, dma_addr=0x12 << PAGE_SHIFT, - length=PAGE_SIZE) - assert ret == 1 - iovec3 = iovec_t() - ret = vfu_sgl_get(ctx, sg3, iovec3) - assert ret == 0 - - # write that spans bytes in bitmap - ret, sg4 = vfu_addr_to_sgl(ctx, dma_addr=0x16 << PAGE_SHIFT, - length=0x4 << PAGE_SHIFT) - assert ret == 1 - iovec4 = iovec_t() - ret = vfu_sgl_get(ctx, sg4, iovec4) - assert ret == 0 - - # not put yet, dirty bitmap should be zero - bitmap = get_dirty_page_bitmap() - assert bitmap == 0b0000000000000000 - - # put SGLs, dirty bitmap should be updated - vfu_sgl_put(ctx, sg1, iovec1) - vfu_sgl_put(ctx, sg4, iovec4) - bitmap = get_dirty_page_bitmap() - assert bitmap == 0b0000001111000001 - - # after another two puts, should just be one dirty page - vfu_sgl_put(ctx, sg2, iovec2) - vfu_sgl_put(ctx, sg3, iovec3) - bitmap = get_dirty_page_bitmap() - assert bitmap == 0b0000000000000100 - - # and should now be clear - bitmap = get_dirty_page_bitmap() - assert bitmap == 0b0000000000000000 - - # - # check various edge cases of bitmap values. - # - - # very first bit - bitmap = write_to_page(ctx, 0x10, 1) - assert bitmap == 0b0000000000000001 - - # top bit of first byte - bitmap = write_to_page(ctx, 0x17, 1) - assert bitmap == 0b0000000010000000 - - # all bits except top one of first byte - bitmap = write_to_page(ctx, 0x10, 7) - assert bitmap == 0b0000000001111111 - - # all bits of first byte - bitmap = write_to_page(ctx, 0x10, 8) - assert bitmap == 0b0000000011111111 - - # all bits of first byte plus bottom bit of next - bitmap = write_to_page(ctx, 0x10, 9) - assert bitmap == 0b0000000111111111 - - # straddle top/bottom bit - bitmap = write_to_page(ctx, 0x17, 2) - assert bitmap == 0b0000000110000000 - - # top bit of second byte - bitmap = write_to_page(ctx, 0x1f, 1) - assert bitmap == 0b1000000000000000 - - # top bit of third byte - bitmap = write_to_page(ctx, 0x27, 1) - assert bitmap == 0b100000000000000000000000 - - # bits in third and first byte - write_to_page(ctx, 0x26, 1, get_bitmap=False) - write_to_page(ctx, 0x12, 2, get_bitmap=False) - bitmap = get_dirty_page_bitmap() - assert bitmap == 0b010000000000000000001100 - - -def test_dirty_pages_stop(): - # FIXME we have a memory leak as we don't free dirty bitmaps when - # destroying the context. - payload = vfio_user_dirty_pages(argsz=len(vfio_user_dirty_pages()), - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload) - - -def test_dirty_pages_start_with_quiesce(): - global quiesce_errno - - quiesce_errno = errno.EBUSY - - payload = vfio_user_dirty_pages(argsz=len(vfio_user_dirty_pages()), - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_START) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, rsp=False, busy=True) - - ret = vfu_device_quiesced(ctx, 0) - assert ret == 0 - - # now should be able to get the reply - get_reply(sock, expect=0) - - quiesce_errno = 0 - - -def test_dirty_pages_bitmap_with_quiesce(): - global quiesce_errno - - quiesce_errno = errno.EBUSY - - ret, sg1 = vfu_addr_to_sgl(ctx, dma_addr=0x10 << PAGE_SHIFT, - length=PAGE_SIZE) - assert ret == 1 - iovec1 = iovec_t() - ret = vfu_sgl_get(ctx, sg1, iovec1) - assert ret == 0 - vfu_sgl_put(ctx, sg1, iovec1) - - bitmap = get_dirty_page_bitmap() - assert bitmap == 0b0000000000000001 - - -def test_dirty_pages_stop_with_quiesce(): - global quiesce_errno - - quiesce_errno = errno.EBUSY - - payload = vfio_user_dirty_pages(argsz=len(vfio_user_dirty_pages()), - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, rsp=False, busy=True) - - ret = vfu_device_quiesced(ctx, 0) - assert ret == 0 - - # now should be able to get the reply - get_reply(sock, expect=0) - - quiesce_errno = 0 - - -def test_dirty_pages_cleanup(): - disconnect_client(ctx, sock) - vfu_destroy_ctx(ctx) - -# ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: diff --git a/test/py/test_dma_unmap.py b/test/py/test_dma_unmap.py index 063dedc..f9529fb 100644 --- a/test/py/test_dma_unmap.py +++ b/test/py/test_dma_unmap.py @@ -113,26 +113,6 @@ def test_dma_unmap_dirty_not_tracking(): expect=errno.EINVAL) -def test_dma_unmap_dirty_not_mapped(): - - setup_dma_regions([(PAGE_SIZE, PAGE_SIZE)]) - vfu_setup_device_migration_callbacks(ctx, offset=PAGE_SIZE) - payload = vfio_user_dirty_pages(argsz=len(vfio_user_dirty_pages()), - flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_START) - - msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload) - - argsz = len(vfio_user_dma_unmap()) + len(vfio_user_bitmap()) + 8 - unmap = vfio_user_dma_unmap(argsz=argsz, - flags=VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP, addr=PAGE_SIZE, - size=PAGE_SIZE) - bitmap = vfio_user_bitmap(pgsize=PAGE_SIZE, size=8) - payload = bytes(unmap) + bytes(bitmap) + bytes(8) - - msg(ctx, sock, VFIO_USER_DMA_UNMAP, payload, - expect=errno.EINVAL) - - def test_dma_unmap_invalid_flags(): setup_dma_regions() diff --git a/test/py/test_migration.py b/test/py/test_migration.py index 614a615..d8eaf86 100644 --- a/test/py/test_migration.py +++ b/test/py/test_migration.py @@ -27,140 +27,7 @@ # DAMAGE. # -from libvfio_user import * -import ctypes as c -import errno -from unittest.mock import patch +# TODO: this -ctx = None -sock = 0 - - -def setup_function(function): - global ctx, sock - - ctx = vfu_create_ctx(flags=LIBVFIO_USER_FLAG_ATTACH_NB) - assert ctx is not None - - ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_MIGR_REGION_IDX, - size=2 << PAGE_SHIFT, flags=VFU_REGION_FLAG_RW) - assert ret == 0 - - ret = vfu_setup_device_migration_callbacks(ctx) - assert ret == 0 - - vfu_setup_device_quiesce_cb(ctx) - - ret = vfu_realize_ctx(ctx) - assert ret == 0 - - sock = connect_client(ctx) - - -def teardown_function(function): - global ctx - vfu_destroy_ctx(ctx) - - -@patch('libvfio_user.quiesce_cb') -@patch('libvfio_user.migr_trans_cb') -def test_migration_bad_access(mock_trans, mock_quiesce): - """ - Tests that attempting to access the migration state register in an - non-aligned manner fails. - - This test is important because we tell whether we need to quiesce by - checking for a register-sized access, otherwise we'll change migration - state without having quiesced. - """ - global ctx, sock - - data = VFIO_DEVICE_STATE_V1_SAVING.to_bytes(c.sizeof(c.c_int), 'little') - write_region(ctx, sock, VFU_PCI_DEV_MIGR_REGION_IDX, offset=0, - count=len(data)-1, data=data, expect=errno.EINVAL) - - mock_trans.assert_not_called() - - -@patch('libvfio_user.quiesce_cb') -@patch('libvfio_user.migr_trans_cb', return_value=0) -def test_migration_trans_sync(mock_trans, mock_quiesce): - """ - Tests transitioning to the saving state. - """ - - global ctx, sock - - data = VFIO_DEVICE_STATE_V1_SAVING.to_bytes(c.sizeof(c.c_int), 'little') - write_region(ctx, sock, VFU_PCI_DEV_MIGR_REGION_IDX, offset=0, - count=len(data), data=data) - - ret = vfu_run_ctx(ctx) - assert ret == 0 - - -@patch('libvfio_user.migr_trans_cb', side_effect=fail_with_errno(errno.EPERM)) -def test_migration_trans_sync_err(mock_trans): - """ - Tests the device returning an error when the migration state is written to. - """ - - global ctx, sock - - data = VFIO_DEVICE_STATE_V1_SAVING.to_bytes(c.sizeof(c.c_int), 'little') - write_region(ctx, sock, VFU_PCI_DEV_MIGR_REGION_IDX, offset=0, - count=len(data), data=data, expect=errno.EPERM) - - ret = vfu_run_ctx(ctx) - assert ret == 0 - - -@patch('libvfio_user.quiesce_cb', side_effect=fail_with_errno(errno.EBUSY)) -@patch('libvfio_user.migr_trans_cb', return_value=0) -def test_migration_trans_async(mock_trans, mock_quiesce): - """ - Tests transitioning to the saving state where the device is initially busy - quiescing. - """ - - global ctx, sock - mock_quiesce - - data = VFIO_DEVICE_STATE_V1_SAVING.to_bytes(c.sizeof(c.c_int), 'little') - write_region(ctx, sock, VFU_PCI_DEV_MIGR_REGION_IDX, offset=0, - count=len(data), data=data, rsp=False, - busy=True) - - ret = vfu_device_quiesced(ctx, 0) - assert ret == 0 - - get_reply(sock) - - ret = vfu_run_ctx(ctx) - assert ret == 0 - - -@patch('libvfio_user.quiesce_cb', side_effect=fail_with_errno(errno.EBUSY)) -@patch('libvfio_user.migr_trans_cb', side_effect=fail_with_errno(errno.ENOTTY)) -def test_migration_trans_async_err(mock_trans, mock_quiesce): - """ - Tests writing to the migration state register, the device not being able to - immediately quiesce, and then finally the device failing to transition to - the new migration state. - """ - - global ctx, sock - - data = VFIO_DEVICE_STATE_V1_RUNNING.to_bytes(c.sizeof(c.c_int), 'little') - write_region(ctx, sock, VFU_PCI_DEV_MIGR_REGION_IDX, offset=0, - count=len(data), data=data, rsp=False, - busy=True) - - ret = vfu_device_quiesced(ctx, 0) - assert ret == 0 - - print("waiting for reply") - get_reply(sock, errno.ENOTTY) - print("received reply") - -# ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: # +def test_migration(): + pass diff --git a/test/py/test_quiesce.py b/test/py/test_quiesce.py index b1fb2fd..e97c565 100644 --- a/test/py/test_quiesce.py +++ b/test/py/test_quiesce.py @@ -31,7 +31,7 @@ from libvfio_user import * import errno from unittest import mock from unittest.mock import patch - +import tempfile ctx = None @@ -195,36 +195,6 @@ def test_allowed_funcs_in_quiesced_dma_unregister_busy(mock_quiesce, mock_dma_unregister.assert_called_once_with(ctx, mock.ANY) -@patch('libvfio_user.migr_trans_cb', side_effect=_side_effect) -@patch('libvfio_user.quiesce_cb') -def test_allowed_funcs_in_quiesed_migration(mock_quiesce, - mock_trans): - - global ctx, sock - _map_dma_region(ctx, sock) - data = VFIO_DEVICE_STATE_V1_SAVING.to_bytes(c.sizeof(c.c_int), 'little') - write_region(ctx, sock, VFU_PCI_DEV_MIGR_REGION_IDX, offset=0, - count=len(data), data=data) - mock_trans.assert_called_once_with(ctx, VFIO_DEVICE_STATE_V1_SAVING) - - -@patch('libvfio_user.migr_trans_cb', side_effect=_side_effect) -@patch('libvfio_user.quiesce_cb') -def test_allowed_funcs_in_quiesed_migration_busy(mock_quiesce, - mock_trans): - - global ctx, sock - _map_dma_region(ctx, sock) - mock_quiesce.side_effect = fail_with_errno(errno.EBUSY) - data = VFIO_DEVICE_STATE_V1_STOP.to_bytes(c.sizeof(c.c_int), 'little') - write_region(ctx, sock, VFU_PCI_DEV_MIGR_REGION_IDX, offset=0, - count=len(data), data=data, rsp=False, - busy=True) - ret = vfu_device_quiesced(ctx, 0) - assert ret == 0 - mock_trans.assert_called_once_with(ctx, VFIO_DEVICE_STATE_V1_STOP) - - @patch('libvfio_user.reset_cb', side_effect=_side_effect) @patch('libvfio_user.quiesce_cb') def test_allowed_funcs_in_quiesced_reset(mock_quiesce, mock_reset): diff --git a/test/py/test_request_errors.py b/test/py/test_request_errors.py index 79af0f2..8a8c14c 100644 --- a/test/py/test_request_errors.py +++ b/test/py/test_request_errors.py @@ -54,10 +54,6 @@ def setup_function(function): ret = vfu_setup_device_reset_cb(ctx) assert ret == 0 - ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_MIGR_REGION_IDX, - size=2 << PAGE_SHIFT, flags=VFU_REGION_FLAG_RW) - assert ret == 0 - ret = vfu_setup_device_migration_callbacks(ctx) assert ret == 0 @@ -187,53 +183,4 @@ def test_disconnected_socket_quiesce_busy(mock_quiesce): mock_quiesce.assert_called_once_with(ctx) -@patch('libvfio_user.reset_cb') -@patch('libvfio_user.quiesce_cb', side_effect=fail_with_errno(errno.EBUSY)) -@patch('libvfio_user.migr_get_pending_bytes_cb') -def test_reply_fail_quiesce_busy(mock_get_pending_bytes, mock_quiesce, - mock_reset): - """Tests failing to reply and the quiesce callback returning EBUSY.""" - - global ctx, sock - - def get_pending_bytes_side_effect(ctx): - sock.close() - return 0 - mock_get_pending_bytes.side_effect = get_pending_bytes_side_effect - - # read the get_pending_bytes register, it should close the socket causing - # the reply to fail - read_region(ctx, sock, VFU_PCI_DEV_MIGR_REGION_IDX, - vfio_user_migration_info.pending_bytes.offset, - vfio_user_migration_info.pending_bytes.size, rsp=False, - busy=True) - - # vfu_run_ctx will try to reset the context and to do that it needs to - # quiesce the device first - mock_quiesce.assert_called_once_with(ctx) - - # vfu_run_ctx will be returning EBUSY and nothing should have happened - # until the device quiesces - for _ in range(0, 3): - vfu_run_ctx(ctx, errno.EBUSY) - mock_quiesce.assert_called_once_with(ctx) - mock_reset.assert_not_called() - - ret = vfu_device_quiesced(ctx, 0) - assert ret == 0 - - # the device quiesced, reset should should happen now - mock_quiesce.assert_called_once_with(ctx) - mock_reset.assert_called_once_with(ctx, VFU_RESET_LOST_CONN) - - try: - get_reply(sock) - except OSError as e: - assert e.errno == errno.EBADF - else: - assert False - - vfu_run_ctx(ctx, errno.ENOTCONN) - - # ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: # diff --git a/test/py/test_setup_region.py b/test/py/test_setup_region.py index d00de68..750958b 100644 --- a/test/py/test_setup_region.py +++ b/test/py/test_setup_region.py @@ -111,30 +111,6 @@ def test_setup_region_bad_pci(): assert c.get_errno() == errno.EINVAL -def test_setup_region_bad_migr(): - ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_MIGR_REGION_IDX, size=512, - flags=(VFU_REGION_FLAG_RW | VFU_REGION_FLAG_MEM)) - assert ret == -1 - assert c.get_errno() == errno.EINVAL - - f = tempfile.TemporaryFile() - f.truncate(0x2000) - - ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_MIGR_REGION_IDX, size=0x2000, - flags=(VFU_REGION_FLAG_RW | VFU_REGION_FLAG_MEM), - fd=f.fileno()) - assert ret == -1 - assert c.get_errno() == errno.EINVAL - - mmap_areas = [(0x0, 0x1000), (0x1000, 0x1000)] - - ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_MIGR_REGION_IDX, size=0x2000, - flags=(VFU_REGION_FLAG_RW | VFU_REGION_FLAG_MEM), - mmap_areas=mmap_areas, fd=f.fileno()) - assert ret == -1 - assert c.get_errno() == errno.EINVAL - - def test_setup_region_cfg_always_cb_nocb(): ret = vfu_setup_region(ctx, index=VFU_PCI_DEV_CFG_REGION_IDX, size=PCI_CFG_SPACE_EXP_SIZE, cb=None, diff --git a/test/unit-tests.c b/test/unit-tests.c index 0fd4fe7..bf99c60 100644 --- a/test/unit-tests.c +++ b/test/unit-tests.c @@ -398,269 +398,9 @@ typedef struct { int conn_fd; } tran_sock_t; -static void -test_migration_state_transitions(void **state UNUSED) -{ - bool (*f)(uint32_t, uint32_t) = vfio_migr_state_transition_is_valid; - uint32_t i, j; - - /* from stopped (000b): all transitions are invalid except to running */ - assert_true(f(0, 0)); - assert_true(f(0, 1)); - for (i = 2; i < 8; i++) { - assert_false(f(0, i)); - } - - /* from running (001b) */ - assert_true(f(1, 0)); - assert_true(f(1, 1)); - assert_true(f(1, 2)); - assert_true(f(1, 3)); - assert_true(f(1, 4)); - assert_false(f(1, 5)); - assert_true(f(1, 6)); - assert_false(f(1, 5)); - - /* from stop-and-copy (010b) */ - assert_true(f(2, 0)); - assert_true(f(2, 1)); - assert_true(f(2, 2)); - assert_false(f(2, 3)); - assert_false(f(2, 4)); - assert_false(f(2, 5)); - assert_true(f(2, 6)); - assert_false(f(2, 7)); - - /* from pre-copy (011b) */ - assert_true(f(3, 0)); - assert_true(f(3, 1)); - assert_true(f(3, 2)); - assert_false(f(3, 3)); - assert_false(f(3, 4)); - assert_false(f(3, 5)); - assert_true(f(3, 6)); - assert_false(f(3, 7)); - - /* from resuming (100b) */ - assert_false(f(4, 0)); - assert_true(f(4, 1)); - assert_false(f(4, 2)); - assert_false(f(4, 3)); - assert_true(f(4, 4)); - assert_false(f(4, 5)); - assert_true(f(4, 6)); - assert_false(f(4, 7)); - - /* - * Transitioning to any other state from the remaining 3 states - * (101b - invalid, 110b - error, 111b - invalid) is invalid. - * Transitioning from the error state to the stopped state is possible but - * that requires a device reset, so we don't consider it a valid state - * transition. - */ - for (i = 5; i < 8; i++) { - for (j = 0; j < 8; j++) { - assert_false(f(i, j)); - } - } -} - -static struct test_setup_migr_reg_dat { - vfu_ctx_t *v; - size_t rs; /* migration registers size */ - size_t ds; /* migration data size */ - size_t s; /* migration region size*/ - const vfu_migration_callbacks_t c; -} migr_reg_data = { - .c = { - .version = VFU_MIGR_CALLBACKS_VERS, - .transition = (void *)0x1, - .get_pending_bytes = (void *)0x2, - .prepare_data = (void *)0x3, - .read_data = (void *)0x4, - .write_data = (void *)0x5, - .data_written = (void *)0x6 - } -}; - -static int -setup_test_setup_migration_region(void **state) -{ - struct test_setup_migr_reg_dat *p = &migr_reg_data; - p->v = vfu_create_ctx(VFU_TRANS_SOCK, "test", 0, NULL, - VFU_DEV_TYPE_PCI); - if (p->v == NULL) { - return -1; - } - p->rs = ROUND_UP(sizeof(struct vfio_user_migration_info), - sysconf(_SC_PAGE_SIZE)); - p->ds = sysconf(_SC_PAGE_SIZE); - p->s = p->rs + p->ds; - *state = p; - return setup(state); -} - -static vfu_ctx_t * -get_vfu_ctx(void **state) -{ - return (*((struct test_setup_migr_reg_dat **)(state)))->v; -} - -static int -teardown_test_setup_migration_region(void **state) -{ - struct test_setup_migr_reg_dat *p = *state; - vfu_destroy_ctx(p->v); - return 0; -} +// TODO: migration state transition tests -static void -test_setup_migration_region_size_ok(void **state) -{ - vfu_ctx_t *v = get_vfu_ctx(state); - int r = vfu_setup_region(v, VFU_PCI_DEV_MIGR_REGION_IDX, - vfu_get_migr_register_area_size(), NULL, - VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1, 0); - assert_int_equal(0, r); -} - -static void -test_setup_migration_region_sparsely_mappable_valid(void **state) -{ - struct test_setup_migr_reg_dat *p = *state; - struct iovec mmap_areas[] = { - [0] = { - .iov_base = (void *)p->rs, - .iov_len = p->ds - } - }; - int r = vfu_setup_region(p->v, VFU_PCI_DEV_MIGR_REGION_IDX, p->s, NULL, - VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, mmap_areas, 1, - 0xdeadbeef, 0); - assert_int_equal(0, r); -} - -static void -test_setup_migration_callbacks_without_migration_region(void **state) -{ - struct test_setup_migr_reg_dat *p = *state; - assert_int_equal(-1, vfu_setup_device_migration_callbacks(p->v, &p->c, 0)); - assert_int_equal(EINVAL, errno); -} - -static void -test_setup_migration_callbacks_bad_data_offset(void **state) -{ - struct test_setup_migr_reg_dat *p = *state; - int r = vfu_setup_region(p->v, VFU_PCI_DEV_MIGR_REGION_IDX, p->s, NULL, - VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1, 0); - assert_int_equal(0, r); - r = vfu_setup_device_migration_callbacks(p->v, &p->c, - vfu_get_migr_register_area_size() - 1); - assert_int_equal(-1, r); -} - -static void -test_setup_migration_callbacks(void **state) -{ - struct test_setup_migr_reg_dat *p = *state; - int r = vfu_setup_region(p->v, VFU_PCI_DEV_MIGR_REGION_IDX, p->s, NULL, - VFU_REGION_FLAG_READ | VFU_REGION_FLAG_WRITE, NULL, 0, -1, 0); - assert_int_equal(0, r); - r = vfu_setup_device_migration_callbacks(p->v, &p->c, - vfu_get_migr_register_area_size()); - assert_int_equal(0, r); - assert_non_null(p->v->migration); - /* FIXME can't validate p->v->migration because it's a private strcut, need to move it out of lib/migration.c */ -} - -static void -test_device_is_stopped_and_copying(UNUSED void **state) -{ - assert_false(device_is_stopped_and_copying(vfu_ctx.migration)); - assert_false(device_is_stopped(vfu_ctx.migration)); - - size_t i; - struct migration migration; - vfu_ctx.migration = &migration; - for (i = 0; i < ARRAY_SIZE(migr_states); i++) { - if (migr_states[i].name == NULL) { - continue; - } - migration.info.device_state = i; - bool r = device_is_stopped_and_copying(vfu_ctx.migration); - if (i == VFIO_DEVICE_STATE_V1_SAVING) { - assert_true(r); - } else { - assert_false(r); - } - r = device_is_stopped(vfu_ctx.migration); - if (i == VFIO_DEVICE_STATE_V1_STOP) { - assert_true(r); - } else { - assert_false(r); - } - } - vfu_ctx.migration = NULL; -} - -static void -test_cmd_allowed_when_stopped_and_copying(UNUSED void **state) -{ - size_t i; - - for (i = 0; i < VFIO_USER_MAX; i++) { - bool r = cmd_allowed_when_stopped_and_copying(i); - if (i == VFIO_USER_REGION_READ || i == VFIO_USER_REGION_WRITE || - i == VFIO_USER_DIRTY_PAGES) { - assert_true(r); - } else { - assert_false(r); - } - } -} - -static void -test_should_exec_command(UNUSED void **state) -{ - struct migration migration = { { 0 } }; - - vfu_ctx.migration = &migration; - - patch("device_is_stopped_and_copying"); - patch("cmd_allowed_when_stopped_and_copying"); - patch("device_is_stopped"); - - /* TEST stopped and copying, command allowed */ - will_return(device_is_stopped_and_copying, true); - expect_value(device_is_stopped_and_copying, migration, &migration); - will_return(cmd_allowed_when_stopped_and_copying, true); - expect_value(cmd_allowed_when_stopped_and_copying, cmd, 0xbeef); - assert_true(should_exec_command(&vfu_ctx, 0xbeef)); - - /* TEST stopped and copying, command not allowed */ - will_return(device_is_stopped_and_copying, true); - expect_any(device_is_stopped_and_copying, migration); - will_return(cmd_allowed_when_stopped_and_copying, false); - expect_any(cmd_allowed_when_stopped_and_copying, cmd); - assert_false(should_exec_command(&vfu_ctx, 0xbeef)); - - /* TEST stopped */ - will_return(device_is_stopped_and_copying, false); - expect_any(device_is_stopped_and_copying, migration); - will_return(device_is_stopped, true); - expect_value(device_is_stopped, migration, &migration); - will_return(cmd_allowed_when_stopped_and_copying, false); - expect_value(cmd_allowed_when_stopped_and_copying, cmd, 0xbeef); - assert_false(should_exec_command(&vfu_ctx, 0xbeef)); - - /* TEST none of the above */ - will_return(device_is_stopped_and_copying, false); - expect_any(device_is_stopped_and_copying, migration); - will_return(device_is_stopped, false); - expect_any(device_is_stopped, migration); - assert_true(should_exec_command(&vfu_ctx, 0xbeef)); -} +// TODO: test migration functions int main(void) @@ -674,26 +414,7 @@ main(void) cmocka_unit_test_setup(test_dma_controller_remove_region_mapped, setup), cmocka_unit_test_setup(test_dma_controller_remove_region_unmapped, setup), cmocka_unit_test_setup(test_dma_addr_to_sgl, setup), - cmocka_unit_test_setup(test_vfu_setup_device_dma, setup), - cmocka_unit_test_setup(test_migration_state_transitions, setup), - cmocka_unit_test_setup_teardown(test_setup_migration_region_size_ok, - setup_test_setup_migration_region, - teardown_test_setup_migration_region), - cmocka_unit_test_setup_teardown(test_setup_migration_region_sparsely_mappable_valid, - setup_test_setup_migration_region, - teardown_test_setup_migration_region), - cmocka_unit_test_setup_teardown(test_setup_migration_callbacks_without_migration_region, - setup_test_setup_migration_region, - teardown_test_setup_migration_region), - cmocka_unit_test_setup_teardown(test_setup_migration_callbacks_bad_data_offset, - setup_test_setup_migration_region, - teardown_test_setup_migration_region), - cmocka_unit_test_setup_teardown(test_setup_migration_callbacks, - setup_test_setup_migration_region, - teardown_test_setup_migration_region), - cmocka_unit_test_setup(test_device_is_stopped_and_copying, setup), - cmocka_unit_test_setup(test_cmd_allowed_when_stopped_and_copying, setup), - cmocka_unit_test_setup(test_should_exec_command, setup), + cmocka_unit_test_setup(test_vfu_setup_device_dma, setup) }; return cmocka_run_group_tests(tests, NULL, NULL); |