[v2,8/9] test/vfio_user: introduce functional test

Message ID 20210114061411.39166-9-chenbo.xia@intel.com (mailing list archive)
State Changes Requested, archived
Delegated to: Thomas Monjalon
Headers
Series Introduce vfio-user library |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Chenbo Xia Jan. 14, 2021, 6:14 a.m. UTC
  This patch introduces functional test for vfio_user client and
server. Note that the test can only be run with server and client
both started and server should be started first.

Signed-off-by: Chenbo Xia <chenbo.xia@intel.com>
Signed-off-by: Xiuchun Lu <xiuchun.lu@intel.com>
---
 app/test/meson.build      |   4 +
 app/test/test_vfio_user.c | 665 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 669 insertions(+)
 create mode 100644 app/test/test_vfio_user.c
  

Comments

David Christensen Jan. 14, 2021, 7:03 p.m. UTC | #1
On 1/13/21 10:14 PM, Chenbo Xia wrote:
> This patch introduces functional test for vfio_user client and
> server. Note that the test can only be run with server and client
> both started and server should be started first.

Receiving a build warning on RHEL 8.3 (gcc 8.3.1) for POWER with this patch:

../app/test/test_vfio_user.c: In function ‘test_dev_cfg_rw’:
../app/test/test_vfio_user.c:60:3: warning: implicit declaration of 
function ‘memcpy’ [-Wimplicit-function-declaration]
    memcpy(buf, loc, count);
    ^~~~~~
../app/test/test_vfio_user.c:60:3: warning: incompatible implicit 
declaration of built-in function ‘memcpy’
../app/test/test_vfio_user.c:60:3: note: include ‘<string.h>’ or provide 
a declaration of ‘memcpy’
../app/test/test_vfio_user.c:18:1:
+#include <string.h>

../app/test/test_vfio_user.c:60:3:
    memcpy(buf, loc, count);
    ^~~~~~
../app/test/test_vfio_user.c:64:2: warning: incompatible implicit 
declaration of built-in function ‘memcpy’
   memcpy(loc, buf, count);
   ^~~~~~
../app/test/test_vfio_user.c:64:2: note: include ‘<string.h>’ or provide 
a declaration of ‘memcpy’
../app/test/test_vfio_user.c: In function ‘test_get_mem’:
../app/test/test_vfio_user.c:192:2: warning: incompatible implicit 
declaration of built-in function ‘memcpy’
   memcpy(server_mem->entry, mem->entry, entry_sz);
   ^~~~~~
../app/test/test_vfio_user.c:192:2: note: include ‘<string.h>’ or 
provide a declaration of ‘memcpy’
../app/test/test_vfio_user.c: In function ‘test_create_device’:
../app/test/test_vfio_user.c:226:6: warning: implicit declaration of 
function ‘strcmp’ [-Wimplicit-function-declaration]
   if (strcmp(sock, test_sock)) {
       ^~~~~~

Also, when running vfio_user_autotest_server, I'm unable to exit the 
application with CTRL-C directly.  If a run a second test with 
vfio_user_autotest_client then the server test runs to completion 
without an error and I'm able to exit the test app normally.  Any way to 
get out of the server test without running the matching client test?

Dave
  
Chenbo Xia Jan. 19, 2021, 3:27 a.m. UTC | #2
Hi David,

> -----Original Message-----
> From: dev <dev-bounces@dpdk.org> On Behalf Of David Christensen
> Sent: Friday, January 15, 2021 3:03 AM
> To: dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v2 8/9] test/vfio_user: introduce functional
> test
> 
> 
> 
> On 1/13/21 10:14 PM, Chenbo Xia wrote:
> > This patch introduces functional test for vfio_user client and
> > server. Note that the test can only be run with server and client
> > both started and server should be started first.
> 
> Receiving a build warning on RHEL 8.3 (gcc 8.3.1) for POWER with this patch:
> 
> ../app/test/test_vfio_user.c: In function ‘test_dev_cfg_rw’:
> ../app/test/test_vfio_user.c:60:3: warning: implicit declaration of
> function ‘memcpy’ [-Wimplicit-function-declaration]
>     memcpy(buf, loc, count);
>     ^~~~~~
> ../app/test/test_vfio_user.c:60:3: warning: incompatible implicit
> declaration of built-in function ‘memcpy’
> ../app/test/test_vfio_user.c:60:3: note: include ‘<string.h>’ or provide
> a declaration of ‘memcpy’
> ../app/test/test_vfio_user.c:18:1:
> +#include <string.h>
> 
> ../app/test/test_vfio_user.c:60:3:
>     memcpy(buf, loc, count);
>     ^~~~~~
> ../app/test/test_vfio_user.c:64:2: warning: incompatible implicit
> declaration of built-in function ‘memcpy’
>    memcpy(loc, buf, count);
>    ^~~~~~
> ../app/test/test_vfio_user.c:64:2: note: include ‘<string.h>’ or provide
> a declaration of ‘memcpy’
> ../app/test/test_vfio_user.c: In function ‘test_get_mem’:
> ../app/test/test_vfio_user.c:192:2: warning: incompatible implicit
> declaration of built-in function ‘memcpy’
>    memcpy(server_mem->entry, mem->entry, entry_sz);
>    ^~~~~~
> ../app/test/test_vfio_user.c:192:2: note: include ‘<string.h>’ or
> provide a declaration of ‘memcpy’
> ../app/test/test_vfio_user.c: In function ‘test_create_device’:
> ../app/test/test_vfio_user.c:226:6: warning: implicit declaration of
> function ‘strcmp’ [-Wimplicit-function-declaration]
>    if (strcmp(sock, test_sock)) {
>        ^~~~~~

Will fix in new version.

> 
> Also, when running vfio_user_autotest_server, I'm unable to exit the
> application with CTRL-C directly.  If a run a second test with
> vfio_user_autotest_client then the server test runs to completion
> without an error and I'm able to exit the test app normally.  Any way to
> get out of the server test without running the matching client test?

Oops..I didn't realize it cannot exit with ctrl-C. It should be fixed.
And normally, because this library has a client/server model, we need a server
and client both launched to complete the test.

Thanks!
Chenbo

> 
> Dave
  
David Christensen Jan. 19, 2021, 6:26 p.m. UTC | #3
On 1/18/21 7:27 PM, Xia, Chenbo wrote:
>> Also, when running vfio_user_autotest_server, I'm unable to exit the
>> application with CTRL-C directly.  If a run a second test with
>> vfio_user_autotest_client then the server test runs to completion
>> without an error and I'm able to exit the test app normally.  Any way to
>> get out of the server test without running the matching client test?
> 
> Oops..I didn't realize it cannot exit with ctrl-C. It should be fixed.
> And normally, because this library has a client/server model, we need a server
> and client both launched to complete the test.
> 

Understand the requirement.  Might be nice to include an example of 
running the two instances of dpdk-test with command line parameters. 
I've not seen dpdk-test used in this way before.

Dave
  

Patch

diff --git a/app/test/meson.build b/app/test/meson.build
index 94fd39fecb..f5b15ac44c 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -138,6 +138,7 @@  test_sources = files('commands.c',
 	'test_trace.c',
 	'test_trace_register.c',
 	'test_trace_perf.c',
+	'test_vfio_user.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
@@ -173,6 +174,7 @@  test_deps = ['acl',
 	'ring',
 	'security',
 	'stack',
+	'vfio_user',
 	'telemetry',
 	'timer'
 ]
@@ -266,6 +268,8 @@  fast_tests = [
         ['service_autotest', true],
         ['thash_autotest', true],
         ['trace_autotest', true],
+        ['vfio_user_autotest_client', false],
+        ['vfio_user_autotest_server', false],
 ]
 
 perf_test_names = [
diff --git a/app/test/test_vfio_user.c b/app/test/test_vfio_user.c
new file mode 100644
index 0000000000..c5fc0e842c
--- /dev/null
+++ b/app/test/test_vfio_user.c
@@ -0,0 +1,665 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stdatomic.h>
+#include <sys/eventfd.h>
+#include <sys/mman.h>
+
+#include <rte_vfio_user.h>
+#include <rte_malloc.h>
+#include <rte_hexdump.h>
+#include <rte_pause.h>
+#include <rte_log.h>
+
+#include "test.h"
+
+#define REGION_SIZE 0x100
+
+struct server_mem_tb {
+	uint32_t entry_num;
+	struct rte_vfio_user_mtb_entry entry[];
+};
+
+static const char test_sock[] = "/tmp/dpdk_vfio_test";
+struct server_mem_tb *server_mem;
+int server_irqfd;
+atomic_uint test_failed;
+atomic_uint server_destroyed;
+
+static int
+test_set_dev_info(const char *sock,
+	struct vfio_device_info *info)
+{
+	int ret;
+
+	info->argsz = sizeof(*info);
+	info->flags = VFIO_DEVICE_FLAGS_RESET | VFIO_DEVICE_FLAGS_PCI;
+	info->num_irqs = VFIO_PCI_NUM_IRQS;
+	info->num_regions = VFIO_PCI_NUM_REGIONS;
+	ret = rte_vfio_user_set_dev_info(sock, info);
+	if (ret) {
+		printf("Failed to set device info\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static ssize_t
+test_dev_cfg_rw(struct rte_vfio_user_reg_info *reg, char *buf,
+	size_t count, loff_t pos, bool iswrite)
+{
+	char *loc = (char *)reg->base + pos;
+
+	if (!iswrite) {
+		if (pos + count > reg->info->size)
+			return -1;
+		memcpy(buf, loc, count);
+		return count;
+	}
+
+	memcpy(loc, buf, count);
+	return count;
+}
+
+static int
+test_set_reg_info(const char *sock_addr,
+	struct rte_vfio_user_regions *reg)
+{
+	struct rte_vfio_user_reg_info *reg_info;
+	void *cfg_base = NULL;
+	uint32_t i, j, sz = 0, reg_sz = REGION_SIZE;
+	int ret;
+
+	reg->reg_num = VFIO_PCI_NUM_REGIONS;
+	sz = sizeof(struct vfio_region_info);
+
+	for (i = 0; i < reg->reg_num; i++) {
+		reg_info = &reg->reg_info[i];
+
+		reg_info->info = rte_zmalloc(NULL, sz, 0);
+		if (!reg_info->info) {
+			printf("Failed to alloc vfio region info\n");
+			goto err;
+		}
+
+		reg_info->priv = NULL;
+		reg_info->fd = -1;
+		reg_info->info->argsz = sz;
+		reg_info->info->cap_offset = sz;
+		reg_info->info->index = i;
+		reg_info->info->offset = 0;
+		reg_info->info->flags = VFIO_REGION_INFO_FLAG_READ |
+			VFIO_REGION_INFO_FLAG_WRITE;
+
+		if (i == VFIO_PCI_CONFIG_REGION_INDEX) {
+			cfg_base = rte_zmalloc(NULL, reg_sz, 0);
+			if (!cfg_base) {
+				printf("Failed to alloc cfg space\n");
+				goto err;
+			}
+			reg_info->base = cfg_base;
+			reg_info->rw = test_dev_cfg_rw;
+			reg_info->info->size = reg_sz;
+		} else {
+			reg_info->base = NULL;
+			reg_info->rw = NULL;
+			reg_info->info->size = 0;
+		}
+	}
+
+	ret = rte_vfio_user_set_reg_info(sock_addr, reg);
+	if (ret) {
+		printf("Failed to set region info\n");
+		return -1;
+	}
+
+	return 0;
+err:
+	for (j = 0; j < i; j++)
+		rte_free(reg->reg_info[i].info);
+	rte_free(cfg_base);
+	return -1;
+}
+
+static void
+cleanup_reg(struct rte_vfio_user_regions *reg)
+{
+	struct rte_vfio_user_reg_info *reg_info;
+	uint32_t i;
+
+	for (i = 0; i < reg->reg_num; i++) {
+		reg_info = &reg->reg_info[i];
+
+		rte_free(reg_info->info);
+
+		if (i == VFIO_PCI_CONFIG_REGION_INDEX)
+			rte_free(reg_info->base);
+	}
+}
+
+static int
+test_set_irq_info(const char *sock,
+	struct rte_vfio_user_irq_info *info)
+{
+	struct vfio_irq_info *irq_info;
+	int ret;
+	uint32_t i;
+
+	info->irq_num = VFIO_PCI_NUM_IRQS;
+	for (i = 0; i < info->irq_num; i++) {
+		irq_info = &info->irq_info[i];
+		irq_info->argsz = sizeof(irq_info);
+		irq_info->index = i;
+
+		if (i == VFIO_PCI_MSIX_IRQ_INDEX) {
+			irq_info->flags = VFIO_IRQ_INFO_EVENTFD |
+				VFIO_IRQ_INFO_NORESIZE;
+			irq_info->count = 1;
+		} else {
+			irq_info->flags = 0;
+			irq_info->count = 0;
+		}
+	}
+
+	ret = rte_vfio_user_set_irq_info(sock, info);
+	if (ret) {
+		printf("Failed to set irq info\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_get_mem(int dev_id)
+{
+	const struct rte_vfio_user_mem *mem;
+	uint32_t entry_sz;
+
+	mem = rte_vfio_user_get_mem_table(dev_id);
+	if (!mem) {
+		printf("Failed to get memory table\n");
+		return -1;
+	}
+
+	entry_sz = sizeof(struct rte_vfio_user_mtb_entry) * mem->entry_num;
+	server_mem = rte_zmalloc(NULL, sizeof(*server_mem) + entry_sz, 0);
+
+	memcpy(server_mem->entry, mem->entry, entry_sz);
+	server_mem->entry_num = mem->entry_num;
+
+	return 0;
+}
+
+static int
+test_get_irq(int dev_id)
+{
+	int ret;
+
+	server_irqfd = -1;
+	ret = rte_vfio_user_get_irq(dev_id, VFIO_PCI_MSIX_IRQ_INDEX, 1,
+		&server_irqfd);
+	if (ret) {
+		printf("Failed to get IRQ\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_create_device(int dev_id)
+{
+	char sock[PATH_MAX];
+
+	RTE_LOG(DEBUG, USER1, "Device created\n");
+
+	if (rte_vfio_get_sock_addr(dev_id, sock, sizeof(sock))) {
+		printf("Failed to get socket addr\n");
+		goto err;
+	}
+
+	if (strcmp(sock, test_sock)) {
+		printf("Wrong socket addr\n");
+		goto err;
+	}
+
+	printf("Get socket address: TEST OK\n");
+
+	return 0;
+err:
+	atomic_store(&test_failed, 1);
+	return -1;
+}
+
+static void
+test_destroy_device(int dev_id __rte_unused)
+{
+	int ret;
+
+	RTE_LOG(DEBUG, USER1, "Device destroyed\n");
+
+	ret = test_get_mem(dev_id);
+	if (ret)
+		goto err;
+
+	printf("Get memory table: TEST OK\n");
+
+	ret = test_get_irq(dev_id);
+	if (ret)
+		goto err;
+
+	printf("Get IRQ: TEST OK\n");
+
+	atomic_store(&server_destroyed, 1);
+	return;
+err:
+	atomic_store(&test_failed, 1);
+}
+
+static int
+test_update_device(int dev_id __rte_unused)
+{
+	RTE_LOG(DEBUG, USER1, "Device updated\n");
+
+	return 0;
+}
+
+static int
+test_lock_dp(int dev_id __rte_unused, int lock)
+{
+	RTE_LOG(DEBUG, USER1, "Device data path %slocked\n", lock ? "" : "un");
+	return 0;
+}
+
+static int
+test_reset_device(int dev_id __rte_unused)
+{
+	RTE_LOG(DEBUG, USER1, "Device reset\n");
+	return 0;
+}
+
+const struct rte_vfio_user_notify_ops test_vfio_ops = {
+	.new_device = test_create_device,
+	.destroy_device = test_destroy_device,
+	.update_status = test_update_device,
+	.lock_dp = test_lock_dp,
+	.reset_device = test_reset_device,
+};
+
+static int
+test_vfio_user_server(void)
+{
+	struct vfio_device_info dev_info;
+	struct rte_vfio_user_regions *reg;
+	struct rte_vfio_user_reg_info *reg_info;
+	struct vfio_region_info *info;
+	struct rte_vfio_user_irq_info *irq_info;
+	struct rte_vfio_user_mtb_entry *ent;
+	int ret, err;
+	uint32_t i;
+
+	atomic_init(&test_failed, 0);
+	atomic_init(&server_destroyed, 0);
+
+	ret = rte_vfio_user_register(test_sock, &test_vfio_ops);
+	if (ret) {
+		printf("Failed to register\n");
+		ret = TEST_FAILED;
+		goto err_regis;
+	}
+
+	printf("Register device: TEST OK\n");
+
+	reg = rte_zmalloc(NULL, sizeof(*reg) + VFIO_PCI_NUM_REGIONS *
+		sizeof(struct rte_vfio_user_reg_info), 0);
+	if (!reg) {
+		printf("Failed to alloc regions\n");
+		ret = TEST_FAILED;
+		goto err_reg;
+	}
+
+	irq_info = rte_zmalloc(NULL, sizeof(*irq_info) + VFIO_PCI_NUM_IRQS *
+		sizeof(struct vfio_irq_info), 0);
+	if (!irq_info) {
+		printf("Failed to alloc irq info\n");
+		ret = TEST_FAILED;
+		goto err_irq;
+	}
+
+	if (test_set_dev_info(test_sock, &dev_info)) {
+		ret = TEST_FAILED;
+		goto err_set;
+	}
+
+	printf("Set device info: TEST OK\n");
+
+	if (test_set_reg_info(test_sock, reg)) {
+		ret = TEST_FAILED;
+		goto err_set;
+	}
+
+	printf("Set device info: TEST OK\n");
+
+	if (test_set_irq_info(test_sock, irq_info)) {
+		ret = TEST_FAILED;
+		goto err;
+	}
+
+	printf("Set irq info: TEST OK\n");
+
+	ret = rte_vfio_user_start(test_sock);
+	if (ret) {
+		printf("Failed to start\n");
+		ret = TEST_FAILED;
+		goto err;
+	}
+
+	printf("Start device: TEST OK\n");
+
+	while (atomic_load(&test_failed) == 0 &&
+		atomic_load(&server_destroyed) == 0)
+		rte_pause();
+
+	if (atomic_load(&test_failed) == 1) {
+		printf("Test failed during device running\n");
+		ret = TEST_FAILED;
+		goto err;
+	}
+
+	printf("=================================\n");
+	printf("Device layout:\n");
+	printf("=================================\n");
+	printf("%u regions, %u IRQs\n", dev_info.num_regions,
+		dev_info.num_irqs);
+	printf("=================================\n");
+
+	reg_info = &reg->reg_info[VFIO_PCI_CONFIG_REGION_INDEX];
+	info = reg_info->info;
+	printf("Configuration Space:\nsize : 0x%llx, prot: %s%s\n",
+		info->size,
+		(info->flags & VFIO_REGION_INFO_FLAG_READ) ? "read/" : "",
+		(info->flags & VFIO_REGION_INFO_FLAG_WRITE) ? "write" : "");
+	rte_hexdump(stdout, "Content", (const void *)reg_info->base,
+		info->size);
+
+	printf("=================================\n");
+	printf("DMA memory table (Entry num: %u):\n", server_mem->entry_num);
+
+	for (i = 0; i < server_mem->entry_num; i++) {
+		ent = &server_mem->entry[i];
+		printf("(Entry %u) gpa: 0x%" PRIx64
+			", size: 0x%" PRIx64 ", hva: 0x%" PRIx64 "\n"
+			", mmap_addr: 0x%" PRIx64 ", mmap_size: 0x%" PRIx64
+			", fd: %d\n", i, ent->gpa, ent->size,
+			ent->host_user_addr, (uint64_t)ent->mmap_addr,
+			ent->mmap_size, ent->fd);
+	}
+
+	printf("=================================\n");
+	printf("MSI-X Interrupt:\nNumber: %u, irqfd: %s\n",
+		irq_info->irq_info[VFIO_PCI_MSIX_IRQ_INDEX].count,
+		server_irqfd == -1 ? "Invalid" : "Valid");
+
+	ret = TEST_SUCCESS;
+
+err:
+	cleanup_reg(reg);
+err_set:
+	rte_free(irq_info);
+err_irq:
+	rte_free(reg);
+err_reg:
+	err = rte_vfio_user_unregister(test_sock);
+	if (err)
+		ret = TEST_FAILED;
+	else
+		printf("Unregister device: TEST OK\n");
+err_regis:
+	return ret;
+}
+
+static int
+test_get_dev_info(int dev_id, struct vfio_device_info *info)
+{
+	int ret;
+
+	ret = rte_vfio_user_get_dev_info(dev_id, info);
+	if (ret) {
+		printf("Failed to get device info\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_get_reg_info(int dev_id, struct vfio_region_info *info)
+{
+	int ret, fd = -1;
+
+	info->index = VFIO_PCI_CONFIG_REGION_INDEX;
+	info->argsz = sizeof(*info);
+	ret = rte_vfio_user_get_reg_info(dev_id, info, &fd);
+	if (ret) {
+		printf("Failed to get region info\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_get_irq_info(int dev_id, struct vfio_irq_info *info)
+{
+	int ret;
+
+	info->index = VFIO_PCI_MSIX_IRQ_INDEX;
+	ret = rte_vfio_user_get_irq_info(dev_id, info);
+	if (ret) {
+		printf("Failed to get irq info\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_set_irqs(int dev_id, struct vfio_irq_set *set, int *fd)
+{
+	int ret;
+
+	*fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+	if (*fd < 0) {
+		printf("Failed to create eventfd\n");
+		return -1;
+	}
+
+	set->argsz = sizeof(*set) + sizeof(int);
+	set->count = 1;
+	set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
+	set->index = VFIO_PCI_MSIX_IRQ_INDEX;
+	set->start = 0;
+	memcpy(set->data, fd, sizeof(*fd));
+
+	ret = rte_vfio_user_set_irqs(dev_id, set);
+	if (ret) {
+		printf("Failed to set irqs\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_dma_map_unmap(int dev_id, struct rte_vfio_user_mem_reg *mem)
+{
+	int ret, fd = -1;
+
+	mem->fd_offset = 0;
+	mem->flags = 0;
+	mem->gpa = 0x12345678;
+	mem->protection = PROT_READ | PROT_WRITE;
+	mem->size = 0x10000;
+
+	/* Map -> Unmap -> Map */
+	ret = rte_vfio_user_dma_map(dev_id, mem, &fd, 1);
+	if (ret) {
+		printf("Failed to dma map\n");
+		return -1;
+	}
+
+	ret = rte_vfio_user_dma_unmap(dev_id, mem, 1);
+	if (ret) {
+		printf("Failed to dma unmap\n");
+		return -1;
+	}
+
+	ret = rte_vfio_user_dma_map(dev_id, mem, &fd, 1);
+	if (ret) {
+		printf("Failed to dma re-map\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_region_read_write(int dev_id, void *read_data, uint64_t sz)
+{
+	int ret;
+	uint32_t data = 0x1A2B3C4D, idx = VFIO_PCI_CONFIG_REGION_INDEX;
+
+	ret = rte_vfio_user_region_write(dev_id, idx, 0, 4, (void *)&data);
+	if (ret) {
+		printf("Failed to write region\n");
+		return -1;
+	}
+
+	ret = rte_vfio_user_region_read(dev_id, idx, 0, sz, read_data);
+	if (ret) {
+		printf("Failed to read region\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_vfio_user_client(void)
+{
+	int ret = 0, dev_id, fd = -1;
+	struct vfio_device_info dev_info;
+	struct vfio_irq_info irq_info;
+	struct rte_vfio_user_mem_reg mem;
+	struct vfio_irq_set *set;
+	struct vfio_region_info reg_info;
+	void *data;
+
+	ret = rte_vfio_user_attach_dev(test_sock);
+	if (ret) {
+		printf("Failed to attach device\n");
+		return TEST_FAILED;
+	}
+
+	printf("Attach device: TEST OK\n");
+
+	dev_id = ret;
+	ret = rte_vfio_user_reset(dev_id);
+	if (ret) {
+		printf("Failed to reset device\n");
+		return TEST_FAILED;
+	}
+
+	printf("Reset device: TEST OK\n");
+
+	if (test_get_dev_info(dev_id, &dev_info))
+		return TEST_FAILED;
+
+	printf("Get device info: TEST OK\n");
+
+	if (test_get_reg_info(dev_id, &reg_info))
+		return TEST_FAILED;
+
+	printf("Get region info: TEST OK\n");
+
+	if (test_get_irq_info(dev_id, &irq_info))
+		return TEST_FAILED;
+
+	printf("Get irq info: TEST OK\n");
+
+	set = rte_zmalloc(NULL, sizeof(*set) + sizeof(int), 0);
+	if (!set) {
+		printf("Failed to allocate irq set\n");
+		return TEST_FAILED;
+	}
+
+	data = rte_zmalloc(NULL, reg_info.size, 0);
+	if (!data) {
+		printf("Failed to allocate data\n");
+		ret = TEST_FAILED;
+		goto err_data;
+	}
+
+	if (test_set_irqs(dev_id, set, &fd)) {
+		ret = TEST_FAILED;
+		goto err;
+	}
+
+	printf("Set irqs: TEST OK\n");
+
+	if (test_dma_map_unmap(dev_id, &mem)) {
+		ret = TEST_FAILED;
+		goto err;
+	}
+
+	printf("DMA map/unmap: TEST OK\n");
+
+	if (test_region_read_write(dev_id, data, reg_info.size)) {
+		ret = TEST_FAILED;
+		goto err;
+	}
+
+	printf("Region read/write: TEST OK\n");
+
+	printf("=================================\n");
+	printf("Device layout:\n");
+	printf("=================================\n");
+	printf("%u regions, %u IRQs\n", dev_info.num_regions,
+		dev_info.num_irqs);
+	printf("=================================\n");
+	printf("Configuration Space:\nsize : 0x%llx, prot: %s%s\n",
+		reg_info.size,
+		(reg_info.flags & VFIO_REGION_INFO_FLAG_READ) ? "read/" : "",
+		(reg_info.flags & VFIO_REGION_INFO_FLAG_WRITE) ? "write" : "");
+	rte_hexdump(stdout, "Content", (const void *)data, reg_info.size);
+
+	printf("=================================\n");
+	printf("DMA memory table (Entry num: 1):\ngpa: 0x%" PRIx64
+		", size: 0x%" PRIx64 ", fd: -1, fd_offset:0x%" PRIx64 "\n",
+		mem.gpa, mem.size, mem.fd_offset);
+	printf("=================================\n");
+	printf("MSI-X Interrupt:\nNumber: %u, irqfd: %s\n", irq_info.count,
+		fd == -1 ? "Invalid" : "Valid");
+
+	ret = rte_vfio_user_detach_dev(dev_id);
+	if (ret) {
+		printf("Failed to detach device\n");
+		ret = TEST_FAILED;
+		goto err;
+	}
+
+	printf("Device detach: TEST OK\n");
+err:
+	rte_free(data);
+err_data:
+	rte_free(set);
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(vfio_user_autotest_client, test_vfio_user_client);
+REGISTER_TEST_COMMAND(vfio_user_autotest_server, test_vfio_user_server);