On Wed, Sep 03, 2025 at 10:19:31PM +0200, John Levon wrote:
> From: Mark Cave-Ayland <[email protected]>
> 
> Add a basic test of the vfio-user PCI client implementation.
> 
> Co-authored-by: John Levon <[email protected]>
> Signed-off-by: Mark Cave-Ayland <[email protected]>
> Signed-off-by: John Levon <[email protected]>
> ---
>  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 <[email protected]>
> 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 :|


Reply via email to