From patchwork Thu Jan 14 06:14:10 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chenbo Xia X-Patchwork-Id: 86506 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id ACB0BA0A02; Thu, 14 Jan 2021 07:20:54 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 484F5140DEB; Thu, 14 Jan 2021 07:19:59 +0100 (CET) Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) by mails.dpdk.org (Postfix) with ESMTP id 7F904140DEA for ; Thu, 14 Jan 2021 07:19:57 +0100 (CET) IronPort-SDR: fMfj2S6ojtGb6q+VgjhyZxcJiFMHiRH9URa0EQDG5aDXk4MSDOve8nCnM3TLj+vZ30Dt6javMB Vkfl+hF9TweA== X-IronPort-AV: E=McAfee;i="6000,8403,9863"; a="175735662" X-IronPort-AV: E=Sophos;i="5.79,346,1602572400"; d="scan'208";a="175735662" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Jan 2021 22:19:57 -0800 IronPort-SDR: aca6jh6H5/tMpIIHV/NjAWtpy0KHPqDxeZR37oL6Wm5WLBbTGn+qwdcYqkLyvMqZKtWENABscO 6DyIeuPLUz6w== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.79,346,1602572400"; d="scan'208";a="349069041" Received: from npg-dpdk-virtio-xiachenbo-nw.sh.intel.com ([10.67.119.123]) by orsmga003.jf.intel.com with ESMTP; 13 Jan 2021 22:19:54 -0800 From: Chenbo Xia To: dev@dpdk.org, thomas@monjalon.net, david.marchand@redhat.com Cc: stephen@networkplumber.org, cunming.liang@intel.com, xiuchun.lu@intel.com, miao.li@intel.com, jingjing.wu@intel.com, beilei.xing@intel.com Date: Thu, 14 Jan 2021 14:14:10 +0800 Message-Id: <20210114061411.39166-9-chenbo.xia@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210114061411.39166-1-chenbo.xia@intel.com> References: <20201218073851.93609-1-chenbo.xia@intel.com> <20210114061411.39166-1-chenbo.xia@intel.com> Subject: [dpdk-dev] [PATCH v2 8/9] test/vfio_user: introduce functional test X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" 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 Signed-off-by: Xiuchun Lu --- 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 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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_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_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_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, ®_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);