aboutsummaryrefslogtreecommitdiff
path: root/test/py/test_migration.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/py/test_migration.py')
-rw-r--r--test/py/test_migration.py175
1 files changed, 126 insertions, 49 deletions
diff --git a/test/py/test_migration.py b/test/py/test_migration.py
index 83c941c..0a59bd3 100644
--- a/test/py/test_migration.py
+++ b/test/py/test_migration.py
@@ -1,7 +1,7 @@
#
-# Copyright (c) 2021 Nutanix Inc. All rights reserved.
+# Copyright (c) 2023 Nutanix Inc. All rights reserved.
#
-# Authors: John Levon <john.levon@nutanix.com>
+# Authors: William Henderson <william.henderson@nutanix.com>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@@ -29,11 +29,15 @@
from libvfio_user import *
from collections import deque
+import ctypes
+import errno
ctx = None
sock = None
current_state = None
path = []
+read_data = None
+write_data = None
UNREACHABLE_STATES = {
@@ -68,13 +72,31 @@ def migr_trans_cb(_ctx, state):
@read_data_cb_t
-def migr_read_data_cb(_ctx, _buf, _count, _offset):
- return
+def migr_read_data_cb(_ctx, buf, count):
+ global read_data
+ length = min(count, len(read_data))
+ ctypes.memmove(buf, read_data, length)
+ read_data = None
+ return length
@write_data_cb_t
-def migr_write_data_cb(_ctx, _buf, _count, _offset):
- return
+def migr_write_data_cb(_ctx, buf, count):
+ global write_data
+ write_data = bytes(count)
+ ctypes.memmove(write_data, buf, count)
+ return count
+
+
+def transition_to_state(state, expect=0):
+ feature = vfio_user_device_feature(
+ argsz=len(vfio_user_device_feature()) +
+ len(vfio_user_device_feature_mig_state()),
+ flags=VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_MIG_DEVICE_STATE
+ )
+ payload = vfio_user_device_feature_mig_state(device_state=state)
+ msg(ctx, sock, VFIO_USER_DEVICE_FEATURE, bytes(feature) + bytes(payload),
+ expect=expect)
def test_migration_setup():
@@ -97,40 +119,26 @@ def test_migration_setup():
ret = vfu_realize_ctx(ctx)
assert ret == 0
- sock = connect_client(ctx)
+ sock = connect_client(ctx, 4)
def get_server_shortest_path(a, b, expectA=0, expectB=0):
- global ctx, sock, path
-
- feature = vfio_user_device_feature(
- argsz=len(vfio_user_device_feature()) +
- len(vfio_user_device_feature_mig_state()),
- flags=VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_MIG_DEVICE_STATE
- )
+ global path
if current_state == VFIO_USER_DEVICE_STATE_STOP_COPY and \
a == VFIO_USER_DEVICE_STATE_PRE_COPY:
# The transition STOP_COPY -> PRE_COPY is explicitly blocked so we
# advance one state to get around this in order to set up the test.
- payload = vfio_user_device_feature_mig_state(
- device_state=VFIO_USER_DEVICE_STATE_STOP
- )
- msg(ctx, sock, VFIO_USER_DEVICE_FEATURE,
- bytes(feature) + bytes(payload))
+ transition_to_state(VFIO_USER_DEVICE_STATE_STOP)
- payload = vfio_user_device_feature_mig_state(device_state=a)
- msg(ctx, sock, VFIO_USER_DEVICE_FEATURE, bytes(feature) + bytes(payload),
- expect=expectA)
+ transition_to_state(a, expect=expectA)
if expectA != 0:
return None
path = []
- payload = vfio_user_device_feature_mig_state(device_state=b)
- msg(ctx, sock, VFIO_USER_DEVICE_FEATURE, bytes(feature) + bytes(payload),
- expect=expectB)
+ transition_to_state(b, expect=expectB)
return path.copy()
@@ -149,8 +157,6 @@ def test_migration_shortest_state_transition_paths():
by the implementation correctly follow these rules.
"""
- global ctx, sock
-
# states (vertices)
V = {VFIO_USER_DEVICE_STATE_ERROR, VFIO_USER_DEVICE_STATE_STOP,
VFIO_USER_DEVICE_STATE_RUNNING, VFIO_USER_DEVICE_STATE_STOP_COPY,
@@ -218,38 +224,109 @@ def test_migration_shortest_state_transition_paths():
def test_migration_stop_copy_to_pre_copy_blocked():
- global ctx, sock
+ transition_to_state(VFIO_USER_DEVICE_STATE_STOP_COPY)
+ transition_to_state(VFIO_USER_DEVICE_STATE_PRE_COPY, expect=errno.EINVAL)
- feature = vfio_user_device_feature(
- argsz=len(vfio_user_device_feature()) +
- len(vfio_user_device_feature_mig_state()),
- flags=VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_MIG_DEVICE_STATE
+
+def test_migration_nonexistent_state():
+ transition_to_state(0xabcd, expect=errno.EINVAL)
+
+
+def test_handle_mig_data_read():
+ global read_data
+
+ transition_to_state(VFIO_USER_DEVICE_STATE_RUNNING)
+
+ argsz = len(vfio_user_mig_data())
+
+ payload = vfio_user_mig_data(
+ argsz=argsz,
+ size=4
)
- payload = vfio_user_device_feature_mig_state(
- device_state=VFIO_USER_DEVICE_STATE_STOP_COPY
+
+ data = bytes([0, 1, 2, 3])
+
+ transition_to_state(VFIO_USER_DEVICE_STATE_PRE_COPY)
+ read_data = data
+ result = msg(ctx, sock, VFIO_USER_MIG_DATA_READ, payload)
+ assert len(result) == argsz + 4
+ assert result[argsz:argsz + 4] == data
+
+ transition_to_state(VFIO_USER_DEVICE_STATE_STOP_COPY)
+ read_data = data
+ result = msg(ctx, sock, VFIO_USER_MIG_DATA_READ, payload)
+ assert len(result) == argsz + 4
+ assert result[argsz:argsz + 4] == data
+
+
+def test_handle_mig_data_read_too_long():
+ global read_data
+
+ transition_to_state(VFIO_USER_DEVICE_STATE_RUNNING)
+
+ payload = vfio_user_mig_data(
+ argsz=len(vfio_user_mig_data()),
+ size=8
)
- msg(ctx, sock, VFIO_USER_DEVICE_FEATURE, bytes(feature) + bytes(payload))
- payload = vfio_user_device_feature_mig_state(
- device_state=VFIO_USER_DEVICE_STATE_PRE_COPY
+ # When we set up the tests at the top of this file we specify that the max
+ # data transfer size is 4 bytes. Here we test to check that a transfer of 8
+ # bytes fails.
+
+ transition_to_state(VFIO_USER_DEVICE_STATE_PRE_COPY)
+ read_data = bytes([1, 2, 3, 4, 5, 6, 7, 8])
+ msg(ctx, sock, VFIO_USER_MIG_DATA_READ, payload, expect=errno.EINVAL)
+
+
+def test_handle_mig_data_read_invalid_state():
+ global read_data
+
+ payload = vfio_user_mig_data(
+ argsz=len(vfio_user_mig_data()),
+ size=4
)
- msg(ctx, sock, VFIO_USER_DEVICE_FEATURE, bytes(feature) + bytes(payload),
- expect=22)
+ data = bytes([1, 2, 3, 4])
-def test_migration_nonexistent_state():
- global ctx, sock
+ transition_to_state(VFIO_USER_DEVICE_STATE_RUNNING)
+ read_data = data
+ msg(ctx, sock, VFIO_USER_MIG_DATA_READ, payload, expect=errno.EINVAL)
- feature = vfio_user_device_feature(
- argsz=len(vfio_user_device_feature()) +
- len(vfio_user_device_feature_mig_state()),
- flags=VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_MIG_DEVICE_STATE
+ transition_to_state(VFIO_USER_DEVICE_STATE_STOP)
+ read_data = data
+ msg(ctx, sock, VFIO_USER_MIG_DATA_READ, payload, expect=errno.EINVAL)
+
+
+def test_handle_mig_data_write():
+ payload = vfio_user_mig_data(
+ argsz=len(vfio_user_mig_data()) + 4,
+ size=4
)
- payload = vfio_user_device_feature_mig_state(
- device_state=0xabcd
+
+ data = bytes([1, 2, 3, 4])
+
+ transition_to_state(VFIO_USER_DEVICE_STATE_RESUMING)
+ msg(ctx, sock, VFIO_USER_MIG_DATA_WRITE, bytes(payload) + data)
+ assert write_data == data
+
+
+def test_handle_mig_data_write_invalid_state():
+ payload = vfio_user_mig_data(
+ argsz=len(vfio_user_mig_data()) + 4,
+ size=4
)
- msg(ctx, sock, VFIO_USER_DEVICE_FEATURE, bytes(feature) + bytes(payload),
- expect=22)
+
+ data = bytes([1, 2, 3, 4])
+
+ transition_to_state(VFIO_USER_DEVICE_STATE_RUNNING)
+ msg(ctx, sock, VFIO_USER_MIG_DATA_WRITE, bytes(payload) + data,
+ expect=errno.EINVAL)
+
+
+def test_device_feature_short_write():
+ payload = struct.pack("I", 8)
+
+ msg(ctx, sock, VFIO_USER_DEVICE_FEATURE, payload, expect=errno.EINVAL)
def test_migration_cleanup():