From patchwork Tue Sep 4 13:11:51 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Burakov, Anatoly" X-Patchwork-Id: 44227 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id CE0104F98; Tue, 4 Sep 2018 15:12:06 +0200 (CEST) Received: from mga02.intel.com (mga02.intel.com [134.134.136.20]) by dpdk.org (Postfix) with ESMTP id E96282BA3 for ; Tue, 4 Sep 2018 15:11:59 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by orsmga101.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 04 Sep 2018 06:11:58 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.53,329,1531810800"; d="scan'208";a="82876064" Received: from irvmail001.ir.intel.com ([163.33.26.43]) by fmsmga002.fm.intel.com with ESMTP; 04 Sep 2018 06:11:55 -0700 Received: from sivswdev01.ir.intel.com (sivswdev01.ir.intel.com [10.237.217.45]) by irvmail001.ir.intel.com (8.14.3/8.13.6/MailSET/Hub) with ESMTP id w84DBsks023393; Tue, 4 Sep 2018 14:11:54 +0100 Received: from sivswdev01.ir.intel.com (localhost [127.0.0.1]) by sivswdev01.ir.intel.com with ESMTP id w84DBsib024935; Tue, 4 Sep 2018 14:11:54 +0100 Received: (from aburakov@localhost) by sivswdev01.ir.intel.com with LOCAL id w84DBseH024919; Tue, 4 Sep 2018 14:11:54 +0100 From: Anatoly Burakov To: dev@dpdk.org Cc: laszlo.madarassy@ericsson.com, laszlo.vadkerti@ericsson.com, andras.kovacs@ericsson.com, winnie.tian@ericsson.com, daniel.andrasi@ericsson.com, janos.kobor@ericsson.com, srinath.mannam@broadcom.com, scott.branden@broadcom.com, ajit.khaparde@broadcom.com, keith.wiles@intel.com, bruce.richardson@intel.com, thomas@monjalon.net Date: Tue, 4 Sep 2018 14:11:51 +0100 Message-Id: <5b003e4bf69e6d9f8496916a9e6361d4f8b1a6a1.1536064999.git.anatoly.burakov@intel.com> X-Mailer: git-send-email 1.7.0.7 In-Reply-To: References: In-Reply-To: References: Subject: [dpdk-dev] [PATCH 16/16] test: add unit tests for external memory support X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 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" Add simple unit tests to test external memory support. The tests are pretty basic and mostly consist of checking if invalid API calls are handled correctly, plus a simple allocation/deallocation test for malloc and memzone. Signed-off-by: Anatoly Burakov --- test/test/Makefile | 1 + test/test/autotest_data.py | 14 +- test/test/meson.build | 1 + test/test/test_external_mem.c | 384 ++++++++++++++++++++++++++++++++++ 4 files changed, 396 insertions(+), 4 deletions(-) create mode 100644 test/test/test_external_mem.c diff --git a/test/test/Makefile b/test/test/Makefile index e6967bab6..074ac6e03 100644 --- a/test/test/Makefile +++ b/test/test/Makefile @@ -71,6 +71,7 @@ SRCS-y += test_bitmap.c SRCS-y += test_reciprocal_division.c SRCS-y += test_reciprocal_division_perf.c SRCS-y += test_fbarray.c +SRCS-y += test_external_mem.c SRCS-y += test_ring.c SRCS-y += test_ring_perf.c diff --git a/test/test/autotest_data.py b/test/test/autotest_data.py index f68d9b111..51f8e1689 100644 --- a/test/test/autotest_data.py +++ b/test/test/autotest_data.py @@ -477,10 +477,16 @@ "Report": None, }, { - "Name": "Fbarray autotest", - "Command": "fbarray_autotest", - "Func": default_autotest, - "Report": None, + "Name": "Fbarray autotest", + "Command": "fbarray_autotest", + "Func": default_autotest, + "Report": None, + }, + { + "Name": "External memory autotest", + "Command": "external_mem_autotest", + "Func": default_autotest, + "Report": None, }, # #Please always keep all dump tests at the end and together! diff --git a/test/test/meson.build b/test/test/meson.build index b1dd6eca2..3abf02b71 100644 --- a/test/test/meson.build +++ b/test/test/meson.build @@ -155,6 +155,7 @@ test_names = [ 'eventdev_common_autotest', 'eventdev_octeontx_autotest', 'eventdev_sw_autotest', + 'external_mem_autotest', 'func_reentrancy_autotest', 'flow_classify_autotest', 'hash_scaling_autotest', diff --git a/test/test/test_external_mem.c b/test/test/test_external_mem.c new file mode 100644 index 000000000..5edb5c348 --- /dev/null +++ b/test/test/test_external_mem.c @@ -0,0 +1,384 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" + +#define EXTERNAL_MEM_SZ (RTE_PGSIZE_4K << 10) /* 4M of data */ + +static int +test_invalid_param(void *addr, size_t len, size_t pgsz, rte_iova_t *iova, + int n_pages) +{ + static const char * const names[] = { + NULL, /* NULL name */ + "", /* empty name */ + "this heap name is definitely way too long to be valid" + }; + const char *valid_name = "valid heap name"; + unsigned int i; + + /* check invalid name handling */ + for (i = 0; i < RTE_DIM(names); i++) { + const char *name = names[i]; + + /* these calls may fail for other reasons, so check errno */ + if (rte_malloc_heap_create(name) >= 0 || rte_errno != EINVAL) { + printf("%s():%i: Created heap with invalid name\n", + __func__, __LINE__); + goto fail; + } + + if (rte_malloc_heap_destroy(name) >= 0 || rte_errno != EINVAL) { + printf("%s():%i: Destroyed heap with invalid name\n", + __func__, __LINE__); + goto fail; + } + + if (rte_malloc_heap_get_socket(name) >= 0 || + rte_errno != EINVAL) { + printf("%s():%i: Found socket for heap with invalid name\n", + __func__, __LINE__); + goto fail; + } + + if (rte_malloc_heap_memory_add(name, addr, len, + NULL, 0, pgsz) >= 0 || rte_errno != EINVAL) { + printf("%s():%i: Added memory to heap with invalid name\n", + __func__, __LINE__); + goto fail; + } + if (rte_malloc_heap_memory_remove(name, addr, len) >= 0 || + rte_errno != EINVAL) { + printf("%s():%i: Removed memory from heap with invalid name\n", + __func__, __LINE__); + goto fail; + } + + if (rte_malloc_heap_memory_attach(name, addr, len) >= 0 || + rte_errno != EINVAL) { + printf("%s():%i: Attached memory to heap with invalid name\n", + __func__, __LINE__); + goto fail; + } + if (rte_malloc_heap_memory_detach(name, addr, len) >= 0 || + rte_errno != EINVAL) { + printf("%s():%i: Detached memory from heap with invalid name\n", + __func__, __LINE__); + goto fail; + } + } + + /* do same as above, but with a valid heap name */ + + /* skip create call */ + if (rte_malloc_heap_destroy(valid_name) >= 0 || rte_errno != ENOENT) { + printf("%s():%i: Destroyed heap with invalid name\n", + __func__, __LINE__); + goto fail; + } + if (rte_malloc_heap_get_socket(valid_name) >= 0 || + rte_errno != ENOENT) { + printf("%s():%i: Found socket for heap with invalid name\n", + __func__, __LINE__); + goto fail; + } + + /* these calls may fail for other reasons, so check errno */ + if (rte_malloc_heap_memory_add(valid_name, addr, len, + NULL, 0, pgsz) >= 0 || rte_errno != ENOENT) { + printf("%s():%i: Added memory to non-existent heap\n", + __func__, __LINE__); + goto fail; + } + if (rte_malloc_heap_memory_remove(valid_name, addr, len) >= 0 || + rte_errno != ENOENT) { + printf("%s():%i: Removed memory from non-existent heap\n", + __func__, __LINE__); + goto fail; + } + + if (rte_malloc_heap_memory_attach(valid_name, addr, len) >= 0 || + rte_errno != ENOENT) { + printf("%s():%i: Attached memory to non-existent heap\n", + __func__, __LINE__); + goto fail; + } + if (rte_malloc_heap_memory_detach(valid_name, addr, len) >= 0 || + rte_errno != ENOENT) { + printf("%s():%i: Detached memory from non-existent heap\n", + __func__, __LINE__); + goto fail; + } + + /* create a valid heap but test other invalid parameters */ + if (rte_malloc_heap_create(valid_name) != 0) { + printf("%s():%i: Failed to create valid heap\n", + __func__, __LINE__); + goto fail; + } + + /* zero length */ + if (rte_malloc_heap_memory_add(valid_name, addr, 0, + NULL, 0, pgsz) >= 0 || rte_errno != EINVAL) { + printf("%s():%i: Added memory with invalid parameters\n", + __func__, __LINE__); + goto fail; + } + + if (rte_malloc_heap_memory_remove(valid_name, addr, 0) >= 0 || + rte_errno != EINVAL) { + printf("%s():%i: Removed memory with invalid parameters\n", + __func__, __LINE__); + goto fail; + } + + if (rte_malloc_heap_memory_attach(valid_name, addr, 0) >= 0 || + rte_errno != EINVAL) { + printf("%s():%i: Attached memory with invalid parameters\n", + __func__, __LINE__); + goto fail; + } + if (rte_malloc_heap_memory_detach(valid_name, addr, 0) >= 0 || + rte_errno != EINVAL) { + printf("%s():%i: Detached memory with invalid parameters\n", + __func__, __LINE__); + goto fail; + } + + /* zero address */ + if (rte_malloc_heap_memory_add(valid_name, NULL, len, + NULL, 0, pgsz) >= 0 || rte_errno != EINVAL) { + printf("%s():%i: Added memory with invalid parameters\n", + __func__, __LINE__); + goto fail; + } + + if (rte_malloc_heap_memory_remove(valid_name, NULL, len) >= 0 || + rte_errno != EINVAL) { + printf("%s():%i: Removed memory with invalid parameters\n", + __func__, __LINE__); + goto fail; + } + + if (rte_malloc_heap_memory_attach(valid_name, NULL, len) >= 0 || + rte_errno != EINVAL) { + printf("%s():%i: Attached memory with invalid parameters\n", + __func__, __LINE__); + goto fail; + } + if (rte_malloc_heap_memory_detach(valid_name, NULL, len) >= 0 || + rte_errno != EINVAL) { + printf("%s():%i: Detached memory with invalid parameters\n", + __func__, __LINE__); + goto fail; + } + + /* wrong page count */ + if (rte_malloc_heap_memory_add(valid_name, addr, len, + iova, 0, pgsz) >= 0 || rte_errno != EINVAL) { + printf("%s():%i: Added memory with invalid parameters\n", + __func__, __LINE__); + goto fail; + } + if (rte_malloc_heap_memory_add(valid_name, addr, len, + iova, n_pages - 1, pgsz) >= 0 || rte_errno != EINVAL) { + printf("%s():%i: Added memory with invalid parameters\n", + __func__, __LINE__); + goto fail; + } + if (rte_malloc_heap_memory_add(valid_name, addr, len, + iova, n_pages + 1, pgsz) >= 0 || rte_errno != EINVAL) { + printf("%s():%i: Added memory with invalid parameters\n", + __func__, __LINE__); + goto fail; + } + + /* tests passed, destroy heap */ + if (rte_malloc_heap_destroy(valid_name) != 0) { + printf("%s():%i: Failed to destroy valid heap\n", + __func__, __LINE__); + goto fail; + } + return 0; +fail: + rte_malloc_heap_destroy(valid_name); + return -1; +} + +static int +test_basic(void *addr, size_t len, size_t pgsz, rte_iova_t *iova, int n_pages) +{ + const char *heap_name = "heap"; + void *ptr = NULL; + int socket_id, i; + const struct rte_memzone *mz = NULL; + + /* create heap */ + if (rte_malloc_heap_create(heap_name) != 0) { + printf("%s():%i: Failed to create malloc heap\n", + __func__, __LINE__); + goto fail; + } + + /* get socket ID corresponding to this heap */ + socket_id = rte_malloc_heap_get_socket(heap_name); + if (socket_id < 0) { + printf("%s():%i: cannot find socket for external heap\n", + __func__, __LINE__); + goto fail; + } + + /* heap is empty, so any allocation should fail */ + ptr = rte_malloc_socket("EXTMEM", 64, 0, socket_id); + if (ptr != NULL) { + printf("%s():%i: Allocated from empty heap\n", __func__, + __LINE__); + goto fail; + } + + /* add memory to heap */ + if (rte_malloc_heap_memory_add(heap_name, addr, len, + iova, n_pages, pgsz) != 0) { + printf("%s():%i: Failed to add memory to heap\n", + __func__, __LINE__); + goto fail; + } + + /* check that we can get this memory from EAL now */ + for (i = 0; i < n_pages; i++) { + const struct rte_memseg *ms; + + ms = rte_mem_virt2memseg(RTE_PTR_ADD(addr, pgsz * i), NULL); + if (ms == NULL) { + printf("%s():%i: Failed to retrieve memseg for external mem\n", + __func__, __LINE__); + goto fail; + } + if (ms->iova != iova[i]) { + printf("%s():%i: IOVA mismatch\n", __func__, __LINE__); + goto fail; + } + } + + /* allocate - this now should succeed */ + ptr = rte_malloc_socket("EXTMEM", 64, 0, socket_id); + if (ptr == NULL) { + printf("%s():%i: Failed to allocate from external heap\n", + __func__, __LINE__); + goto fail; + } + + /* check if address is in expected range */ + if (ptr < addr || ptr >= RTE_PTR_ADD(addr, len)) { + printf("%s():%i: Allocated from unexpected address space\n", + __func__, __LINE__); + goto fail; + } + + /* we've allocated something - removing memory should fail */ + if (rte_malloc_heap_memory_remove(heap_name, addr, len) >= 0 || + rte_errno != EBUSY) { + printf("%s():%i: Removing memory succeeded when memory is not free\n", + __func__, __LINE__); + goto fail; + } + if (rte_malloc_heap_destroy(heap_name) >= 0 || rte_errno != EBUSY) { + printf("%s():%i: Destroying heap succeeded when memory is not free\n", + __func__, __LINE__); + goto fail; + } + + /* try allocating an IOVA-contiguous memzone - this should succeed + * because we've set up a contiguous IOVA table. + */ + mz = rte_memzone_reserve("heap_test", pgsz * 2, socket_id, + RTE_MEMZONE_IOVA_CONTIG); + if (mz == NULL) { + printf("%s():%i: Failed to reserve memzone\n", + __func__, __LINE__); + goto fail; + } + + rte_malloc_dump_stats(stdout, NULL); + rte_malloc_dump_heaps(stdout); + + /* free memory - removing it should now succeed */ + rte_free(ptr); + ptr = NULL; + + rte_memzone_free(mz); + mz = NULL; + + if (rte_malloc_heap_memory_remove(heap_name, addr, len) != 0) { + printf("%s():%i: Removing memory from heap failed\n", + __func__, __LINE__); + goto fail; + } + if (rte_malloc_heap_destroy(heap_name) != 0) { + printf("%s():%i: Destroying heap failed\n", + __func__, __LINE__); + goto fail; + } + + return 0; +fail: + rte_memzone_free(mz); + rte_free(ptr); + /* even if something failed, attempt to clean up */ + rte_malloc_heap_memory_remove(heap_name, addr, len); + rte_malloc_heap_destroy(heap_name); + + return -1; +} + +/* we need to test attach/detach in secondary processes. */ +static int +test_external_mem(void) +{ + size_t len = EXTERNAL_MEM_SZ; + size_t pgsz = RTE_PGSIZE_4K; + rte_iova_t iova[len / pgsz]; + void *addr; + int ret, n_pages; + + /* create external memory area */ + n_pages = RTE_DIM(iova); + addr = mmap(NULL, len, PROT_WRITE | PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (addr == MAP_FAILED) { + printf("%s():%i: Failed to create dummy memory area\n", + __func__, __LINE__); + return -1; + } + for (int i = 0; i < n_pages; i++) { + /* arbitrary IOVA */ + rte_iova_t tmp = 0x100000000 + i * pgsz; + iova[i] = tmp; + } + + ret = test_invalid_param(addr, len, pgsz, iova, n_pages); + ret |= test_basic(addr, len, pgsz, iova, n_pages); + + munmap(addr, len); + + return ret; +} + +REGISTER_TEST_COMMAND(external_mem_autotest, test_external_mem);