* [PATCH v4 0/3] vfio-user client functional test
@ 2025-09-03 20:19 John Levon
2025-09-03 20:19 ` [PATCH v4 1/3] tests/functional: return output from cmd.py helpers John Levon
` (3 more replies)
0 siblings, 4 replies; 9+ messages in thread
From: John Levon @ 2025-09-03 20:19 UTC (permalink / raw)
To: qemu-devel
Cc: Cédric Le Goater, Thanos Makatos, Daniel P. Berrangé,
Thomas Huth, Zhao Liu, John Levon, Philippe Mathieu-Daudé,
Paolo Bonzini
Add a basic functional test for the vfio-user client, along with a couple of
test framework extensions to support it.
v4: generalize the test so it's less sensitive to build/environment
John Levon (2):
tests/functional: return output from cmd.py helpers
tests/functional: add vm param to cmd.py helpers
Mark Cave-Ayland (1):
tests/functional: add a vfio-user smoke test
MAINTAINERS | 1 +
tests/functional/qemu_test/cmd.py | 65 +++++-
tests/functional/x86_64/meson.build | 1 +
.../x86_64/test_vfio_user_client.py | 207 ++++++++++++++++++
4 files changed, 262 insertions(+), 12 deletions(-)
create mode 100755 tests/functional/x86_64/test_vfio_user_client.py
--
2.43.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 1/3] tests/functional: return output from cmd.py helpers
2025-09-03 20:19 [PATCH v4 0/3] vfio-user client functional test John Levon
@ 2025-09-03 20:19 ` John Levon
2025-09-08 14:32 ` Daniel P. Berrangé
2025-09-03 20:19 ` [PATCH v4 2/3] tests/functional: add vm param to " John Levon
` (2 subsequent siblings)
3 siblings, 1 reply; 9+ messages in thread
From: John Levon @ 2025-09-03 20:19 UTC (permalink / raw)
To: qemu-devel
Cc: Cédric Le Goater, Thanos Makatos, Daniel P. Berrangé,
Thomas Huth, Zhao Liu, John Levon, Philippe Mathieu-Daudé,
Paolo Bonzini
Tests might want to look at the whole output from a command execution,
as well as just logging it. Add support for this.
Signed-off-by: John Levon <john.levon@nutanix.com>
---
tests/functional/qemu_test/cmd.py | 53 +++++++++++++++++++++++++------
1 file changed, 44 insertions(+), 9 deletions(-)
diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py
index dc5f422b77..c19dfc577f 100644
--- a/tests/functional/qemu_test/cmd.py
+++ b/tests/functional/qemu_test/cmd.py
@@ -45,6 +45,9 @@ def is_readable_executable_file(path):
# If end of line is seen, with neither @success or @failure
# return False
#
+# In both cases, also return the contents of the line (in bytes)
+# up to that point.
+#
# If @failure is seen, then mark @test as failed
def _console_read_line_until_match(test, vm, success, failure):
msg = bytes([])
@@ -76,10 +79,23 @@ def _console_read_line_until_match(test, vm, success, failure):
except:
console_logger.debug(msg)
- return done
+ return done, msg
def _console_interaction(test, success_message, failure_message,
send_string, keep_sending=False, vm=None):
+ """
+ Interact with the console until either message is seen.
+
+ :param success_message: if this message appears, finish interaction
+ :param failure_message: if this message appears, test fails
+ :param send_string: a string to send to the console before trying
+ to read a new line
+ :param keep_sending: keep sending the send string each time
+ :param vm: the VM to interact with
+
+ :return: The collected output (in bytes form).
+ """
+
assert not keep_sending or send_string
assert success_message or send_string
@@ -101,6 +117,8 @@ def _console_interaction(test, success_message, failure_message,
if failure_message is not None:
failure_message_b = failure_message.encode()
+ out = bytes([])
+
while True:
if send_string:
vm.console_socket.sendall(send_string.encode())
@@ -113,11 +131,17 @@ def _console_interaction(test, success_message, failure_message,
break
continue
- if _console_read_line_until_match(test, vm,
- success_message_b,
- failure_message_b):
+ done, line = _console_read_line_until_match(test, vm,
+ success_message_b,
+ failure_message_b)
+
+ out += line
+
+ if done:
break
+ return out
+
def interrupt_interactive_console_until_pattern(test, success_message,
failure_message=None,
interrupt_string='\r'):
@@ -140,10 +164,12 @@ def interrupt_interactive_console_until_pattern(test, success_message,
:param failure_message: if this message appears, test fails
:param interrupt_string: a string to send to the console before trying
to read a new line
+
+ :return: The collected output (in bytes form).
"""
assert success_message
- _console_interaction(test, success_message, failure_message,
- interrupt_string, True)
+ return _console_interaction(test, success_message, failure_message,
+ interrupt_string, True)
def wait_for_console_pattern(test, success_message, failure_message=None,
vm=None):
@@ -155,9 +181,12 @@ def wait_for_console_pattern(test, success_message, failure_message=None,
:type test: :class:`qemu_test.QemuSystemTest`
:param success_message: if this message appears, test succeeds
:param failure_message: if this message appears, test fails
+
+ :return: The collected output (in bytes form).
"""
assert success_message
- _console_interaction(test, success_message, failure_message, None, vm=vm)
+ return _console_interaction(test, success_message, failure_message,
+ None, vm=vm)
def exec_command(test, command):
"""
@@ -168,8 +197,10 @@ def exec_command(test, command):
:type test: :class:`qemu_test.QemuSystemTest`
:param command: the command to send
:type command: str
+
+ :return: The collected output (in bytes form).
"""
- _console_interaction(test, None, None, command + '\r')
+ return _console_interaction(test, None, None, command + '\r')
def exec_command_and_wait_for_pattern(test, command,
success_message, failure_message=None):
@@ -184,9 +215,13 @@ def exec_command_and_wait_for_pattern(test, command,
:param command: the command to send
:param success_message: if this message appears, test succeeds
:param failure_message: if this message appears, test fails
+
+ :return: The collected output (in bytes form).
"""
assert success_message
- _console_interaction(test, success_message, failure_message, command + '\r')
+
+ return _console_interaction(test, success_message, failure_message,
+ command + '\r')
def get_qemu_img(test):
test.log.debug('Looking for and selecting a qemu-img binary')
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 2/3] tests/functional: add vm param to cmd.py helpers
2025-09-03 20:19 [PATCH v4 0/3] vfio-user client functional test John Levon
2025-09-03 20:19 ` [PATCH v4 1/3] tests/functional: return output from cmd.py helpers John Levon
@ 2025-09-03 20:19 ` John Levon
2025-09-08 14:33 ` Daniel P. Berrangé
2025-09-03 20:19 ` [PATCH v4 3/3] tests/functional: add a vfio-user smoke test John Levon
2025-09-09 13:58 ` [PATCH v4 0/3] vfio-user client functional test Thomas Huth
3 siblings, 1 reply; 9+ messages in thread
From: John Levon @ 2025-09-03 20:19 UTC (permalink / raw)
To: qemu-devel
Cc: Cédric Le Goater, Thanos Makatos, Daniel P. Berrangé,
Thomas Huth, Zhao Liu, John Levon, Philippe Mathieu-Daudé,
Paolo Bonzini
Extend the "vm" parameter of wait_for_console_pattern() to all the other
utility functions; this allows them to be used on a VM other than
test.vm.
Signed-off-by: John Levon <john.levon@nutanix.com>
---
tests/functional/qemu_test/cmd.py | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py
index c19dfc577f..8069c89730 100644
--- a/tests/functional/qemu_test/cmd.py
+++ b/tests/functional/qemu_test/cmd.py
@@ -144,7 +144,8 @@ def _console_interaction(test, success_message, failure_message,
def interrupt_interactive_console_until_pattern(test, success_message,
failure_message=None,
- interrupt_string='\r'):
+ interrupt_string='\r',
+ vm=None):
"""
Keep sending a string to interrupt a console prompt, while logging the
console output. Typical use case is to break a boot loader prompt, such:
@@ -164,12 +165,13 @@ def interrupt_interactive_console_until_pattern(test, success_message,
:param failure_message: if this message appears, test fails
:param interrupt_string: a string to send to the console before trying
to read a new line
+ :param vm: VM to use
:return: The collected output (in bytes form).
"""
assert success_message
return _console_interaction(test, success_message, failure_message,
- interrupt_string, True)
+ interrupt_string, True, vm=vm)
def wait_for_console_pattern(test, success_message, failure_message=None,
vm=None):
@@ -181,6 +183,7 @@ def wait_for_console_pattern(test, success_message, failure_message=None,
:type test: :class:`qemu_test.QemuSystemTest`
:param success_message: if this message appears, test succeeds
:param failure_message: if this message appears, test fails
+ :param vm: VM to use
:return: The collected output (in bytes form).
"""
@@ -188,7 +191,7 @@ def wait_for_console_pattern(test, success_message, failure_message=None,
return _console_interaction(test, success_message, failure_message,
None, vm=vm)
-def exec_command(test, command):
+def exec_command(test, command, vm=None):
"""
Send a command to a console (appending CRLF characters), while logging
the content.
@@ -196,14 +199,16 @@ def exec_command(test, command):
:param test: a test containing a VM.
:type test: :class:`qemu_test.QemuSystemTest`
:param command: the command to send
+ :param vm: VM to use
:type command: str
:return: The collected output (in bytes form).
"""
- return _console_interaction(test, None, None, command + '\r')
+ return _console_interaction(test, None, None, command + '\r', vm=vm)
def exec_command_and_wait_for_pattern(test, command,
- success_message, failure_message=None):
+ success_message, failure_message=None,
+ vm=None):
"""
Send a command to a console (appending CRLF characters), then wait
for success_message to appear on the console, while logging the.
@@ -215,13 +220,14 @@ def exec_command_and_wait_for_pattern(test, command,
:param command: the command to send
:param success_message: if this message appears, test succeeds
:param failure_message: if this message appears, test fails
+ :param vm: VM to use
:return: The collected output (in bytes form).
"""
assert success_message
return _console_interaction(test, success_message, failure_message,
- command + '\r')
+ command + '\r', vm=vm)
def get_qemu_img(test):
test.log.debug('Looking for and selecting a qemu-img binary')
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 3/3] tests/functional: add a vfio-user smoke test
2025-09-03 20:19 [PATCH v4 0/3] vfio-user client functional test John Levon
2025-09-03 20:19 ` [PATCH v4 1/3] tests/functional: return output from cmd.py helpers John Levon
2025-09-03 20:19 ` [PATCH v4 2/3] tests/functional: add vm param to " John Levon
@ 2025-09-03 20:19 ` John Levon
2025-09-08 14:42 ` Daniel P. Berrangé
2025-09-09 13:58 ` [PATCH v4 0/3] vfio-user client functional test Thomas Huth
3 siblings, 1 reply; 9+ messages in thread
From: John Levon @ 2025-09-03 20:19 UTC (permalink / raw)
To: qemu-devel
Cc: Cédric Le Goater, Thanos Makatos, Daniel P. Berrangé,
Thomas Huth, Zhao Liu, John Levon, Philippe Mathieu-Daudé,
Paolo Bonzini, Mark Cave-Ayland
From: Mark Cave-Ayland <mark.caveayland@nutanix.com>
Add a basic test of the vfio-user PCI client implementation.
Co-authored-by: John Levon <john.levon@nutanix.com>
Signed-off-by: Mark Cave-Ayland <mark.caveayland@nutanix.com>
Signed-off-by: John Levon <john.levon@nutanix.com>
---
MAINTAINERS | 1 +
tests/functional/x86_64/meson.build | 1 +
.../x86_64/test_vfio_user_client.py | 207 ++++++++++++++++++
3 files changed, 209 insertions(+)
create mode 100755 tests/functional/x86_64/test_vfio_user_client.py
diff --git a/MAINTAINERS b/MAINTAINERS
index 1ae28e8804..9987ac8a4d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4305,6 +4305,7 @@ F: docs/system/devices/vfio-user.rst
F: hw/vfio-user/*
F: include/hw/vfio-user/*
F: subprojects/libvfio-user
+F: tests/functional/x86_64/test_vfio_user_client.py
EBPF:
M: Jason Wang <jasowang@redhat.com>
diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build
index d0b4667bb8..eed1936976 100644
--- a/tests/functional/x86_64/meson.build
+++ b/tests/functional/x86_64/meson.build
@@ -31,6 +31,7 @@ tests_x86_64_system_thorough = [
'replay',
'reverse_debug',
'tuxrun',
+ 'vfio_user_client',
'virtio_balloon',
'virtio_gpu',
]
diff --git a/tests/functional/x86_64/test_vfio_user_client.py b/tests/functional/x86_64/test_vfio_user_client.py
new file mode 100755
index 0000000000..a9cb2f4621
--- /dev/null
+++ b/tests/functional/x86_64/test_vfio_user_client.py
@@ -0,0 +1,207 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2025 Nutanix, Inc.
+#
+# Author:
+# Mark Cave-Ayland <mark.caveayland@nutanix.com>
+# John Levon <john.levon@nutanix.com>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""
+Check basic vfio-user-pci client functionality. The test starts two VMs:
+
+ - the server VM runs the libvfio-user "gpio" example server inside it,
+ piping vfio-user traffic between a local UNIX socket and a virtio-serial
+ port. On the host, the virtio-serial port is backed by a local socket.
+
+ - the client VM loads the gpio-pci-idio-16 kernel module, with the
+ vfio-user client connecting to the above local UNIX socket.
+
+This way, we don't depend on trying to run a vfio-user server on the host
+itself.
+
+Once both VMs are running, we run some basic configuration on the gpio device
+and verify that the server is logging the expected out. As this is consistent
+given the same VM images, we just do a simple direct comparison.
+"""
+
+import difflib
+import logging
+import os
+import re
+import select
+import shutil
+import socket
+import subprocess
+import time
+
+from qemu_test import Asset
+from qemu_test import QemuSystemTest
+from qemu_test import exec_command
+from qemu_test import exec_command_and_wait_for_pattern
+from qemu_test import wait_for_console_pattern
+
+# Exact output can vary, so we just sample for some expected lines.
+EXPECTED_SERVER_LINES = [
+ "gpio: adding DMA region [0, 0xc0000) offset=0 flags=0x3",
+ "gpio: devinfo flags 0x3, num_regions 9, num_irqs 5",
+ "gpio: region_info[0] offset 0 flags 0 size 0 argsz 32",
+ "gpio: region_info[1] offset 0 flags 0 size 0 argsz 32",
+ "gpio: region_info[2] offset 0 flags 0x3 size 256 argsz 32",
+ "gpio: region_info[3] offset 0 flags 0 size 0 argsz 32",
+ "gpio: region_info[4] offset 0 flags 0 size 0 argsz 32",
+ "gpio: region_info[5] offset 0 flags 0 size 0 argsz 32",
+ "gpio: region_info[7] offset 0 flags 0x3 size 256 argsz 32",
+ "gpio: region7: read 256 bytes at 0",
+ "gpio: region7: read 0 from (0x30:4)",
+ "gpio: cleared EROM",
+ "gpio: I/O space enabled",
+ "gpio: memory space enabled",
+ "gpio: SERR# enabled",
+ "gpio: region7: wrote 0x103 to (0x4:2)",
+ "gpio: I/O space enabled",
+ "gpio: memory space enabled",
+]
+
+class VfioUserClient(QemuSystemTest):
+
+ ASSET_REPO = 'https://github.com/mcayland-ntx/libvfio-user-test'
+
+ ASSET_KERNEL = Asset(
+ f'{ASSET_REPO}/raw/refs/heads/main/images/bzImage',
+ '40292fa6ce95d516e26bccf5974e138d0db65a6de0bc540cabae060fe9dea605'
+ )
+
+ ASSET_ROOTFS = Asset(
+ f'{ASSET_REPO}/raw/refs/heads/main/images/rootfs.ext2',
+ 'e1e3abae8aebb8e6e77f08b1c531caeacf46250c94c815655c6bbea59fc3d1c1'
+ )
+
+
+ def prepare_images(self):
+ """Set up the images for the VMs."""
+ self.kernel_path = self.ASSET_KERNEL.fetch()
+ rootfs_path = self.ASSET_ROOTFS.fetch()
+
+ self.server_rootfs_path = self.scratch_file('server.ext2')
+ shutil.copy(rootfs_path, self.server_rootfs_path)
+ os.chmod(self.server_rootfs_path, 0o600)
+ self.client_rootfs_path = self.scratch_file('client.ext2')
+ shutil.copy(rootfs_path, self.client_rootfs_path)
+ os.chmod(self.client_rootfs_path, 0o600)
+
+ def configure_server_vm_args(self, server_vm, sock_path):
+ """
+ Configuration for the server VM. Set up virtio-serial device backed by
+ the given socket path.
+ """
+ server_vm.add_args('-kernel', self.kernel_path)
+ server_vm.add_args('-append', 'console=ttyS0 root=/dev/sda')
+ server_vm.add_args('-drive',
+ f"file={self.server_rootfs_path},if=ide,format=raw,id=drv0")
+ server_vm.add_args('-snapshot')
+ server_vm.add_args('-chardev',
+ f"socket,id=sock0,path={sock_path},telnet=off,server=on,wait=off")
+ server_vm.add_args('-device', 'virtio-serial')
+ server_vm.add_args('-device',
+ 'virtserialport,chardev=sock0,name=org.fedoraproject.port.0')
+
+ def configure_client_vm_args(self, client_vm, sock_path):
+ """
+ Configuration for the client VM. Point the vfio-user-pci device to the
+ socket path configured above.
+ """
+
+ client_vm.add_args('-kernel', self.kernel_path)
+ client_vm.add_args('-append', 'console=ttyS0 root=/dev/sda')
+ client_vm.add_args('-drive',
+ f'file={self.client_rootfs_path},if=ide,format=raw,id=drv0')
+ client_vm.add_args('-device',
+ '{"driver":"vfio-user-pci",' +
+ '"socket":{"path": "%s", "type": "unix"}}' % sock_path)
+
+ def setup_vfio_user_pci_server(self, server_vm):
+ """
+ Start the libvfio-user server within the server VM, and arrange
+ for data to shuttle between its socket and the virtio serial port.
+ """
+ wait_for_console_pattern(self, 'login:', None, server_vm)
+ exec_command_and_wait_for_pattern(self, 'root', '#', None, server_vm)
+
+ exec_command_and_wait_for_pattern(self,
+ 'gpio-pci-idio-16 -v /tmp/vfio-user.sock >/var/tmp/gpio.out 2>&1 &',
+ '#', None, server_vm)
+ # wait for libvfio-user to initialize properly
+ exec_command_and_wait_for_pattern(self, 'sleep 5', '#', None, server_vm)
+ exec_command_and_wait_for_pattern(self,
+ 'socat UNIX-CONNECT:/tmp/vfio-user.sock /dev/vport0p1,ignoreeof ' +
+ ' &', '#', None, server_vm)
+
+ def test_vfio_user_pci(self):
+ self.prepare_images()
+ self.set_machine('pc')
+ self.require_device('virtio-serial')
+ self.require_device('vfio-user-pci')
+
+ sock_dir = self.socket_dir()
+ socket_path = sock_dir.name + '/vfio-user.sock'
+ socket_path = '/tmp/vfio-user.sock'
+
+ server_vm = self.get_vm(name='server')
+ server_vm.set_console()
+ self.configure_server_vm_args(server_vm, socket_path)
+
+ server_vm.launch()
+
+ self.log.debug('starting libvfio-user server')
+
+ self.setup_vfio_user_pci_server(server_vm)
+
+ client_vm = self.get_vm(name="client")
+ client_vm.set_console()
+ self.configure_client_vm_args(client_vm, socket_path)
+
+ try:
+ client_vm.launch()
+ except:
+ self.log.error('client VM failed to start, dumping server logs')
+ exec_command_and_wait_for_pattern(self, 'cat /var/tmp/gpio.out',
+ '#', None, server_vm)
+ raise
+
+ self.log.debug('waiting for client VM boot')
+
+ wait_for_console_pattern(self, 'login:', None, client_vm)
+ exec_command_and_wait_for_pattern(self, 'root', '#', None, client_vm)
+
+ #
+ # Here, we'd like to actually interact with the gpio device a little
+ # more as described at:
+ #
+ # https://github.com/nutanix/libvfio-user/blob/master/docs/qemu.md
+ #
+ # Unfortunately, the buildroot Linux kernel has some undiagnosed issue
+ # so we don't get /sys/class/gpio. Nonetheless just the basic
+ # initialization and setup is enough for basic testing of vfio-user.
+ #
+
+ self.log.debug('collecting libvfio-user server output')
+
+ out = exec_command_and_wait_for_pattern(self,
+ 'cat /var/tmp/gpio.out',
+ 'gpio: region2: wrote 0 to (0x1:1)',
+ None, server_vm)
+
+ pattern = re.compile(r'^gpio:')
+
+ gpio_server_out = [s for s in out.decode().splitlines()
+ if pattern.search(s)]
+
+ for line in EXPECTED_SERVER_LINES:
+ if line not in gpio_server_out:
+ self.log.error(f'Missing server debug line: {line}')
+ self.fail(False)
+
+
+if __name__ == '__main__':
+ QemuSystemTest.main()
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/3] tests/functional: return output from cmd.py helpers
2025-09-03 20:19 ` [PATCH v4 1/3] tests/functional: return output from cmd.py helpers John Levon
@ 2025-09-08 14:32 ` Daniel P. Berrangé
0 siblings, 0 replies; 9+ messages in thread
From: Daniel P. Berrangé @ 2025-09-08 14:32 UTC (permalink / raw)
To: John Levon
Cc: qemu-devel, Cédric Le Goater, Thanos Makatos, Thomas Huth,
Zhao Liu, Philippe Mathieu-Daudé, Paolo Bonzini
On Wed, Sep 03, 2025 at 10:19:29PM +0200, John Levon wrote:
> Tests might want to look at the whole output from a command execution,
> as well as just logging it. Add support for this.
>
> Signed-off-by: John Levon <john.levon@nutanix.com>
> ---
> tests/functional/qemu_test/cmd.py | 53 +++++++++++++++++++++++++------
> 1 file changed, 44 insertions(+), 9 deletions(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 2/3] tests/functional: add vm param to cmd.py helpers
2025-09-03 20:19 ` [PATCH v4 2/3] tests/functional: add vm param to " John Levon
@ 2025-09-08 14:33 ` Daniel P. Berrangé
0 siblings, 0 replies; 9+ messages in thread
From: Daniel P. Berrangé @ 2025-09-08 14:33 UTC (permalink / raw)
To: John Levon
Cc: qemu-devel, Cédric Le Goater, Thanos Makatos, Thomas Huth,
Zhao Liu, Philippe Mathieu-Daudé, Paolo Bonzini
On Wed, Sep 03, 2025 at 10:19:30PM +0200, John Levon wrote:
> Extend the "vm" parameter of wait_for_console_pattern() to all the other
> utility functions; this allows them to be used on a VM other than
> test.vm.
>
> Signed-off-by: John Levon <john.levon@nutanix.com>
> ---
> tests/functional/qemu_test/cmd.py | 18 ++++++++++++------
> 1 file changed, 12 insertions(+), 6 deletions(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 3/3] tests/functional: add a vfio-user smoke test
2025-09-03 20:19 ` [PATCH v4 3/3] tests/functional: add a vfio-user smoke test John Levon
@ 2025-09-08 14:42 ` Daniel P. Berrangé
2025-09-09 14:44 ` John Levon
0 siblings, 1 reply; 9+ messages in thread
From: Daniel P. Berrangé @ 2025-09-08 14:42 UTC (permalink / raw)
To: John Levon
Cc: qemu-devel, Cédric Le Goater, Thanos Makatos, Thomas Huth,
Zhao Liu, Philippe Mathieu-Daudé, Paolo Bonzini,
Mark Cave-Ayland
On Wed, Sep 03, 2025 at 10:19:31PM +0200, John Levon wrote:
> From: Mark Cave-Ayland <mark.caveayland@nutanix.com>
>
> Add a basic test of the vfio-user PCI client implementation.
>
> Co-authored-by: John Levon <john.levon@nutanix.com>
> Signed-off-by: Mark Cave-Ayland <mark.caveayland@nutanix.com>
> Signed-off-by: John Levon <john.levon@nutanix.com>
> ---
> MAINTAINERS | 1 +
> tests/functional/x86_64/meson.build | 1 +
> .../x86_64/test_vfio_user_client.py | 207 ++++++++++++++++++
> 3 files changed, 209 insertions(+)
> create mode 100755 tests/functional/x86_64/test_vfio_user_client.py
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1ae28e8804..9987ac8a4d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4305,6 +4305,7 @@ F: docs/system/devices/vfio-user.rst
> F: hw/vfio-user/*
> F: include/hw/vfio-user/*
> F: subprojects/libvfio-user
> +F: tests/functional/x86_64/test_vfio_user_client.py
>
> EBPF:
> M: Jason Wang <jasowang@redhat.com>
> diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build
> index d0b4667bb8..eed1936976 100644
> --- a/tests/functional/x86_64/meson.build
> +++ b/tests/functional/x86_64/meson.build
> @@ -31,6 +31,7 @@ tests_x86_64_system_thorough = [
> 'replay',
> 'reverse_debug',
> 'tuxrun',
> + 'vfio_user_client',
> 'virtio_balloon',
> 'virtio_gpu',
> ]
> diff --git a/tests/functional/x86_64/test_vfio_user_client.py b/tests/functional/x86_64/test_vfio_user_client.py
> new file mode 100755
> index 0000000000..a9cb2f4621
> --- /dev/null
> +++ b/tests/functional/x86_64/test_vfio_user_client.py
> @@ -0,0 +1,207 @@
> + def prepare_images(self):
> + """Set up the images for the VMs."""
> + self.kernel_path = self.ASSET_KERNEL.fetch()
> + rootfs_path = self.ASSET_ROOTFS.fetch()
> +
> + self.server_rootfs_path = self.scratch_file('server.ext2')
> + shutil.copy(rootfs_path, self.server_rootfs_path)
> + os.chmod(self.server_rootfs_path, 0o600)
> + self.client_rootfs_path = self.scratch_file('client.ext2')
> + shutil.copy(rootfs_path, self.client_rootfs_path)
> + os.chmod(self.client_rootfs_path, 0o600)
So copying the read-only asset to a writable file in the scratchdir....
> +
> + def configure_server_vm_args(self, server_vm, sock_path):
> + """
> + Configuration for the server VM. Set up virtio-serial device backed by
> + the given socket path.
> + """
> + server_vm.add_args('-kernel', self.kernel_path)
> + server_vm.add_args('-append', 'console=ttyS0 root=/dev/sda')
> + server_vm.add_args('-drive',
> + f"file={self.server_rootfs_path},if=ide,format=raw,id=drv0")
> + server_vm.add_args('-snapshot')
..but here you're using -snapshot, so surely the copying of the asset
into the scratch dir is not required ?
> + server_vm.add_args('-chardev',
> + f"socket,id=sock0,path={sock_path},telnet=off,server=on,wait=off")
> + server_vm.add_args('-device', 'virtio-serial')
> + server_vm.add_args('-device',
> + 'virtserialport,chardev=sock0,name=org.fedoraproject.port.0')
> +
> + def configure_client_vm_args(self, client_vm, sock_path):
> + """
> + Configuration for the client VM. Point the vfio-user-pci device to the
> + socket path configured above.
> + """
> +
> + client_vm.add_args('-kernel', self.kernel_path)
> + client_vm.add_args('-append', 'console=ttyS0 root=/dev/sda')
> + client_vm.add_args('-drive',
> + f'file={self.client_rootfs_path},if=ide,format=raw,id=drv0')
...but no using of -snapshot here, so copying the asset would be
required?
Can we just use -snapshot in both cases & avoid the copying ?
> + client_vm.add_args('-device',
> + '{"driver":"vfio-user-pci",' +
> + '"socket":{"path": "%s", "type": "unix"}}' % sock_path)
> +
> + def setup_vfio_user_pci_server(self, server_vm):
> + """
> + Start the libvfio-user server within the server VM, and arrange
> + for data to shuttle between its socket and the virtio serial port.
> + """
> + wait_for_console_pattern(self, 'login:', None, server_vm)
> + exec_command_and_wait_for_pattern(self, 'root', '#', None, server_vm)
> +
> + exec_command_and_wait_for_pattern(self,
> + 'gpio-pci-idio-16 -v /tmp/vfio-user.sock >/var/tmp/gpio.out 2>&1 &',
> + '#', None, server_vm)
> + # wait for libvfio-user to initialize properly
> + exec_command_and_wait_for_pattern(self, 'sleep 5', '#', None, server_vm)
> + exec_command_and_wait_for_pattern(self,
> + 'socat UNIX-CONNECT:/tmp/vfio-user.sock /dev/vport0p1,ignoreeof ' +
> + ' &', '#', None, server_vm)
Hardcoded socket paths in /tmp ...
> +
> + def test_vfio_user_pci(self):
> + self.prepare_images()
> + self.set_machine('pc')
> + self.require_device('virtio-serial')
> + self.require_device('vfio-user-pci')
> +
> + sock_dir = self.socket_dir()
> + socket_path = sock_dir.name + '/vfio-user.sock'
> + socket_path = '/tmp/vfio-user.sock'
This isn't honouring the temporary dir for the socket files.
This temp dir needs to be passed into setup_vfio_user_pci_server
> +
> + server_vm = self.get_vm(name='server')
> + server_vm.set_console()
> + self.configure_server_vm_args(server_vm, socket_path)
> +
> + server_vm.launch()
> +
> + self.log.debug('starting libvfio-user server')
> +
> + self.setup_vfio_user_pci_server(server_vm)
> +
> + client_vm = self.get_vm(name="client")
> + client_vm.set_console()
> + self.configure_client_vm_args(client_vm, socket_path)
> +
> + try:
> + client_vm.launch()
> + except:
> + self.log.error('client VM failed to start, dumping server logs')
> + exec_command_and_wait_for_pattern(self, 'cat /var/tmp/gpio.out',
> + '#', None, server_vm)
> + raise
> +
> + self.log.debug('waiting for client VM boot')
> +
> + wait_for_console_pattern(self, 'login:', None, client_vm)
> + exec_command_and_wait_for_pattern(self, 'root', '#', None, client_vm)
> +
> + #
> + # Here, we'd like to actually interact with the gpio device a little
> + # more as described at:
> + #
> + # https://github.com/nutanix/libvfio-user/blob/master/docs/qemu.md
> + #
> + # Unfortunately, the buildroot Linux kernel has some undiagnosed issue
> + # so we don't get /sys/class/gpio. Nonetheless just the basic
> + # initialization and setup is enough for basic testing of vfio-user.
> + #
> +
> + self.log.debug('collecting libvfio-user server output')
> +
> + out = exec_command_and_wait_for_pattern(self,
> + 'cat /var/tmp/gpio.out',
> + 'gpio: region2: wrote 0 to (0x1:1)',
> + None, server_vm)
> +
> + pattern = re.compile(r'^gpio:')
Use of 're' is overkill here...
> +
> + gpio_server_out = [s for s in out.decode().splitlines()
> + if pattern.search(s)]
...... as this can just use s.startswith("gpio:")
> +
> + for line in EXPECTED_SERVER_LINES:
> + if line not in gpio_server_out:
> + self.log.error(f'Missing server debug line: {line}')
> + self.fail(False)
> +
> +
> +if __name__ == '__main__':
> + QemuSystemTest.main()
> --
> 2.43.0
>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 0/3] vfio-user client functional test
2025-09-03 20:19 [PATCH v4 0/3] vfio-user client functional test John Levon
` (2 preceding siblings ...)
2025-09-03 20:19 ` [PATCH v4 3/3] tests/functional: add a vfio-user smoke test John Levon
@ 2025-09-09 13:58 ` Thomas Huth
3 siblings, 0 replies; 9+ messages in thread
From: Thomas Huth @ 2025-09-09 13:58 UTC (permalink / raw)
To: John Levon, qemu-devel
Cc: Cédric Le Goater, Thanos Makatos, Daniel P. Berrangé,
Zhao Liu, Philippe Mathieu-Daudé, Paolo Bonzini,
Mark Cave-Ayland, Vladimir Sementsov-Ogievskiy
On 03/09/2025 22.19, John Levon wrote:
> Add a basic functional test for the vfio-user client, along with a couple of
> test framework extensions to support it.
>
> v4: generalize the test so it's less sensitive to build/environment
>
> John Levon (2):
> tests/functional: return output from cmd.py helpers
> tests/functional: add vm param to cmd.py helpers
>
> Mark Cave-Ayland (1):
> tests/functional: add a vfio-user smoke test
Hi,
FYI, I've added the first two patches to my current pull request from today
(since there's another series on the list that needs similar changes to
cmd.py, so we can hopefully get the conflict sorted that way).
For the third patch, I assume this will go via the vfio-user tree once
you've addressed Daniels review comments - if not, let me know, then I can
pick it up the next time.
Thomas
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 3/3] tests/functional: add a vfio-user smoke test
2025-09-08 14:42 ` Daniel P. Berrangé
@ 2025-09-09 14:44 ` John Levon
0 siblings, 0 replies; 9+ messages in thread
From: John Levon @ 2025-09-09 14:44 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Cédric Le Goater, Thanos Makatos, Thomas Huth,
Zhao Liu, Philippe Mathieu-Daudé, Paolo Bonzini,
Mark Cave-Ayland
On Mon, Sep 08, 2025 at 03:42:35PM +0100, Daniel P. Berrangé wrote:
> ...but no using of -snapshot here, so copying the asset would be
> required?
>
> Can we just use -snapshot in both cases & avoid the copying ?
Yes; thanks.
> > + def setup_vfio_user_pci_server(self, server_vm):
> > + for data to shuttle between its socket and the virtio serial port.
> > + """
> > + wait_for_console_pattern(self, 'login:', None, server_vm)
> > + exec_command_and_wait_for_pattern(self, 'root', '#', None, server_vm)
> > +
> > + exec_command_and_wait_for_pattern(self,
> > + 'gpio-pci-idio-16 -v /tmp/vfio-user.sock >/var/tmp/gpio.out 2>&1 &',
> > + '#', None, server_vm)
> > + # wait for libvfio-user to initialize properly
> > + exec_command_and_wait_for_pattern(self, 'sleep 5', '#', None, server_vm)
> > + exec_command_and_wait_for_pattern(self,
> > + 'socat UNIX-CONNECT:/tmp/vfio-user.sock /dev/vport0p1,ignoreeof ' +
> > + ' &', '#', None, server_vm)
>
> Hardcoded socket paths in /tmp ...
This is fine: we're inside the server VM at this point, so can't affect anything
else.
> > + sock_dir = self.socket_dir()
> > + socket_path = sock_dir.name + '/vfio-user.sock'
> > + socket_path = '/tmp/vfio-user.sock'
>
> This isn't honouring the temporary dir for the socket files.
Stray debug line left in, sorry.
> This temp dir needs to be passed into setup_vfio_user_pci_server
See above, this is a different socket to the one inside the VM.
> > + pattern = re.compile(r'^gpio:')
>
> Use of 're' is overkill here...
Sure, thanks.
regards
john
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-09-09 14:46 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-03 20:19 [PATCH v4 0/3] vfio-user client functional test John Levon
2025-09-03 20:19 ` [PATCH v4 1/3] tests/functional: return output from cmd.py helpers John Levon
2025-09-08 14:32 ` Daniel P. Berrangé
2025-09-03 20:19 ` [PATCH v4 2/3] tests/functional: add vm param to " John Levon
2025-09-08 14:33 ` Daniel P. Berrangé
2025-09-03 20:19 ` [PATCH v4 3/3] tests/functional: add a vfio-user smoke test John Levon
2025-09-08 14:42 ` Daniel P. Berrangé
2025-09-09 14:44 ` John Levon
2025-09-09 13:58 ` [PATCH v4 0/3] vfio-user client functional test Thomas Huth
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).