[v16,7/9] dma/skeleton: add test cases

Message ID 1629689494-55091-8-git-send-email-fengchengwen@huawei.com (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers
Series support dmadev |

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

fengchengwen Aug. 23, 2021, 3:31 a.m. UTC
  Patch introduces dmadev unit testcase for validation against the
skeleton dmadev PMD implementation.

Test cases are added along with the skeleton driver implementation.
It can be enabled by using vdev argument to any DPDK binary:

	--vdev="dma_skeleton,selftest=1"

In case 'selftest=1' is not provided, autotest doesn't execute the
test cases but the vdev is still available for application use.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 drivers/dma/skeleton/meson.build            |   1 +
 drivers/dma/skeleton/skeleton_dmadev.c      |  34 +-
 drivers/dma/skeleton/skeleton_dmadev.h      |   1 +
 drivers/dma/skeleton/skeleton_dmadev_test.c | 521 ++++++++++++++++++++++++++++
 4 files changed, 553 insertions(+), 4 deletions(-)
 create mode 100644 drivers/dma/skeleton/skeleton_dmadev_test.c
  

Comments

Bruce Richardson Aug. 23, 2021, 2:03 p.m. UTC | #1
On Mon, Aug 23, 2021 at 11:31:32AM +0800, Chengwen Feng wrote:
> Patch introduces dmadev unit testcase for validation against the
> skeleton dmadev PMD implementation.
> 
> Test cases are added along with the skeleton driver implementation.
> It can be enabled by using vdev argument to any DPDK binary:
> 
> 	--vdev="dma_skeleton,selftest=1"
> 
> In case 'selftest=1' is not provided, autotest doesn't execute the
> test cases but the vdev is still available for application use.
> 
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> ---

Having self-tests was useful for rawdev implementations as each rawdev was
(potentially) a completely different device type so no common set of tests
could really be written for them. However, for dmadev, we have a common set
of functions and APIs, so I think that the tests provided here should be in
a general test-case set.

We are preparing to upstream a test suite for DMA devices, based off the
work we have in progress on our own drivers, and we'll look to include some
of the tests from here in that for consistency.  Please wait for that set -
hopefully appearing the next day or two - before doing additional work on
this set, as our set includes quite a comprehensive set of functional tests
in it, which would go well with the API tests here.

In terms of the skeleton dmadev itself, I'm not fully convinced of its
usefulness, but it does allow unit testing of the APIs in the absense of
dma device hardware. Whether that utility is worth the maintenance cost,
though, I'm not sure.

Regards,
/Bruce
  
fengchengwen Aug. 26, 2021, 9:30 a.m. UTC | #2
The skeleton_dma mainly focus on dma framework ut, as it currently functions.

Agree add more general testcase which run with diffenernt hardware drivers, these
testcases mainly used to test drivers (not dma framework). In this
way, the driver's selftest ops can be implemented very easily.


On 2021/8/23 22:03, Bruce Richardson wrote:
> On Mon, Aug 23, 2021 at 11:31:32AM +0800, Chengwen Feng wrote:
>> Patch introduces dmadev unit testcase for validation against the
>> skeleton dmadev PMD implementation.
>>
>> Test cases are added along with the skeleton driver implementation.
>> It can be enabled by using vdev argument to any DPDK binary:
>>
>> 	--vdev="dma_skeleton,selftest=1"
>>
>> In case 'selftest=1' is not provided, autotest doesn't execute the
>> test cases but the vdev is still available for application use.
>>
>> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
>> ---
> 
> Having self-tests was useful for rawdev implementations as each rawdev was
> (potentially) a completely different device type so no common set of tests
> could really be written for them. However, for dmadev, we have a common set
> of functions and APIs, so I think that the tests provided here should be in
> a general test-case set.
> 
> We are preparing to upstream a test suite for DMA devices, based off the
> work we have in progress on our own drivers, and we'll look to include some
> of the tests from here in that for consistency.  Please wait for that set -
> hopefully appearing the next day or two - before doing additional work on
> this set, as our set includes quite a comprehensive set of functional tests
> in it, which would go well with the API tests here.
> 
> In terms of the skeleton dmadev itself, I'm not fully convinced of its
> usefulness, but it does allow unit testing of the APIs in the absense of
> dma device hardware. Whether that utility is worth the maintenance cost,
> though, I'm not sure.
> 
> Regards,
> /Bruce
> .
>
  

Patch

diff --git a/drivers/dma/skeleton/meson.build b/drivers/dma/skeleton/meson.build
index 27509b1..5d47339 100644
--- a/drivers/dma/skeleton/meson.build
+++ b/drivers/dma/skeleton/meson.build
@@ -4,4 +4,5 @@ 
 deps += ['dmadev', 'kvargs', 'ring', 'bus_vdev']
 sources = files(
         'skeleton_dmadev.c',
+        'skeleton_dmadev_test.c',
 )
diff --git a/drivers/dma/skeleton/skeleton_dmadev.c b/drivers/dma/skeleton/skeleton_dmadev.c
index b3ab4a0..1707e88 100644
--- a/drivers/dma/skeleton/skeleton_dmadev.c
+++ b/drivers/dma/skeleton/skeleton_dmadev.c
@@ -430,6 +430,7 @@  static const struct rte_dmadev_ops skeldma_ops = {
 	.stats_reset = skeldma_stats_reset,
 
 	.dev_dump = skeldma_dump,
+	.dev_selftest = test_dma_skeleton,
 };
 
 static int
@@ -503,11 +504,24 @@  skeldma_parse_lcore(const char *key __rte_unused,
 	return 0;
 }
 
+static int
+skeldma_parse_selftest(const char *key __rte_unused,
+		       const char *value,
+		       void *opaque)
+{
+	int flag = atoi(value);
+	if (flag == 0 || flag == 1)
+		*(int *)opaque = flag;
+	return 0;
+}
+
 static void
-skeldma_parse_vdev_args(struct rte_vdev_device *vdev, int *lcore_id)
+skeldma_parse_vdev_args(struct rte_vdev_device *vdev,
+			int *lcore_id, int *selftest)
 {
 	static const char *const args[] = {
 		SKELDMA_ARG_LCORE,
+		SKELDMA_ARG_SELFTEST,
 		NULL
 	};
 
@@ -524,8 +538,11 @@  skeldma_parse_vdev_args(struct rte_vdev_device *vdev, int *lcore_id)
 
 	(void)rte_kvargs_process(kvlist, SKELDMA_ARG_LCORE,
 				 skeldma_parse_lcore, lcore_id);
+	(void)rte_kvargs_process(kvlist, SKELDMA_ARG_SELFTEST,
+				 skeldma_parse_selftest, selftest);
 
-	SKELDMA_INFO("Parse lcore_id = %d\n", *lcore_id);
+	SKELDMA_INFO("Parse lcore_id = %d selftest = %d\n",
+		     *lcore_id, *selftest);
 
 	rte_kvargs_free(kvlist);
 }
@@ -535,6 +552,7 @@  skeldma_probe(struct rte_vdev_device *vdev)
 {
 	const char *name;
 	int lcore_id = -1;
+	int selftest = 0;
 	int ret;
 
 	name = rte_vdev_device_name(vdev);
@@ -552,10 +570,17 @@  skeldma_probe(struct rte_vdev_device *vdev)
 		return -EINVAL;
 	}
 
-	skeldma_parse_vdev_args(vdev, &lcore_id);
+	skeldma_parse_vdev_args(vdev, &lcore_id, &selftest);
 
 	ret = skeldma_create(name, vdev, lcore_id);
 	if (ret >= 0) {
+		/* In case command line argument for 'selftest' was passed;
+		 * if invalid arguments were passed, execution continues but
+		 * without selftest.
+		 */
+		if (selftest)
+			(void)test_dma_skeleton(ret);
+
 		SKELDMA_INFO("Create %s dmadev lcore-id %d\n", name, lcore_id);
 		/* Device instance created; Second instance not possible */
 		skeldma_init_once = 1;
@@ -592,4 +617,5 @@  static struct rte_vdev_driver skeldma_pmd_drv = {
 RTE_LOG_REGISTER_DEFAULT(skeldma_logtype, INFO);
 RTE_PMD_REGISTER_VDEV(dma_skeleton, skeldma_pmd_drv);
 RTE_PMD_REGISTER_PARAM_STRING(dma_skeleton,
-		SKELDMA_ARG_LCORE "=<uint16> ");
+		SKELDMA_ARG_LCORE "=<uint16> "
+		SKELDMA_ARG_SELFTEST "=<0|1> ");
diff --git a/drivers/dma/skeleton/skeleton_dmadev.h b/drivers/dma/skeleton/skeleton_dmadev.h
index 6495653..e8a310d 100644
--- a/drivers/dma/skeleton/skeleton_dmadev.h
+++ b/drivers/dma/skeleton/skeleton_dmadev.h
@@ -22,6 +22,7 @@  extern int skeldma_logtype;
 	SKELDMA_LOG(ERR, fmt, ## args)
 
 #define SKELDMA_ARG_LCORE	"lcore"
+#define SKELDMA_ARG_SELFTEST	"selftest"
 
 struct skeldma_desc {
 	void *src;
diff --git a/drivers/dma/skeleton/skeleton_dmadev_test.c b/drivers/dma/skeleton/skeleton_dmadev_test.c
new file mode 100644
index 0000000..be56f07
--- /dev/null
+++ b/drivers/dma/skeleton/skeleton_dmadev_test.c
@@ -0,0 +1,521 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 HiSilicon Limited.
+ */
+
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_malloc.h>
+#include <rte_test.h>
+
+/* Using relative path as skeleton_dmadev is not part of exported headers */
+#include "skeleton_dmadev.h"
+
+#define SKELDMA_TEST_DEBUG(fmt, args...) \
+	SKELDMA_LOG(DEBUG, fmt, ## args)
+#define SKELDMA_TEST_INFO(fmt, args...) \
+	SKELDMA_LOG(INFO, fmt, ## args)
+
+#define SKELDMA_TEST_RUN(test) \
+	testsuite_run_test(test, #test)
+
+#define TEST_MEMCPY_SIZE	1024
+#define TEST_WAIT_US_VAL	50000
+
+#define TEST_SUCCESS 0
+#define TEST_FAILED  -1
+
+static uint16_t test_dev_id;
+static uint16_t invalid_dev_id;
+
+static int total;
+static int passed;
+static int failed;
+static char *src;
+static char *dst;
+
+static int
+testsuite_setup(uint16_t dev_id)
+{
+	test_dev_id = dev_id;
+	invalid_dev_id = RTE_DMADEV_MAX_DEVS;
+
+	src = rte_malloc("dmadev_test_src", TEST_MEMCPY_SIZE, 0);
+	if (src == NULL)
+		return -ENOMEM;
+	dst = rte_malloc("dmadev_test_dst", TEST_MEMCPY_SIZE, 0);
+	if (dst == NULL)
+		return -ENOMEM;
+
+	total = 0;
+	passed = 0;
+	failed = 0;
+
+	return 0;
+}
+
+static void
+testsuite_teardown(void)
+{
+	rte_free(src);
+	rte_free(dst);
+	/* Ensure the dmadev is stopped. */
+	rte_dmadev_stop(test_dev_id);
+}
+
+static void
+testsuite_run_test(int (*test)(void), const char *name)
+{
+	int ret = 0;
+
+	if (test) {
+		ret = test();
+		if (ret < 0) {
+			failed++;
+			SKELDMA_TEST_INFO("%s Failed", name);
+		} else {
+			passed++;
+			SKELDMA_TEST_DEBUG("%s Passed", name);
+		}
+	}
+
+	total++;
+}
+
+static int
+test_dmadev_get_dev_id(void)
+{
+	int ret = rte_dmadev_get_dev_id("invalid_dmadev_device");
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	return TEST_SUCCESS;
+}
+
+static int
+test_dmadev_is_valid_dev(void)
+{
+	int ret;
+	ret = rte_dmadev_is_valid_dev(invalid_dev_id);
+	RTE_TEST_ASSERT(ret == false, "Expected false for invalid dev id");
+	ret = rte_dmadev_is_valid_dev(test_dev_id);
+	RTE_TEST_ASSERT(ret == true, "Expected true for valid dev id");
+	return TEST_SUCCESS;
+}
+
+static int
+test_dmadev_count(void)
+{
+	uint16_t count = rte_dmadev_count();
+	RTE_TEST_ASSERT(count > 0, "Invalid dmadev count %u", count);
+	return TEST_SUCCESS;
+}
+
+static int
+test_dmadev_info_get(void)
+{
+	struct rte_dmadev_info info =  { 0 };
+	int ret;
+
+	ret = rte_dmadev_info_get(invalid_dev_id, &info);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	ret = rte_dmadev_info_get(test_dev_id, NULL);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	ret = rte_dmadev_info_get(test_dev_id, &info);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to obtain device info");
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_dmadev_configure(void)
+{
+	struct rte_dmadev_conf conf = { 0 };
+	struct rte_dmadev_info info = { 0 };
+	int ret;
+
+	/* Check for invalid parameters */
+	ret = rte_dmadev_configure(invalid_dev_id, &conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	ret = rte_dmadev_configure(test_dev_id, NULL);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Check for nb_vchans == 0 */
+	memset(&conf, 0, sizeof(conf));
+	ret = rte_dmadev_configure(test_dev_id, &conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Check for conf.nb_vchans > info.max_vchans */
+	ret = rte_dmadev_info_get(test_dev_id, &info);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to obtain device info");
+	memset(&conf, 0, sizeof(conf));
+	conf.nb_vchans = info.max_vchans + 1;
+	ret = rte_dmadev_configure(test_dev_id, &conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Check enable silent mode */
+	memset(&conf, 0, sizeof(conf));
+	conf.nb_vchans = info.max_vchans;
+	conf.enable_silent = true;
+	ret = rte_dmadev_configure(test_dev_id, &conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Configure success */
+	memset(&conf, 0, sizeof(conf));
+	conf.nb_vchans = info.max_vchans;
+	ret = rte_dmadev_configure(test_dev_id, &conf);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure dmadev, %d", ret);
+
+	/* Check configure success */
+	ret = rte_dmadev_info_get(test_dev_id, &info);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to obtain device info");
+	RTE_TEST_ASSERT_EQUAL(conf.nb_vchans, info.nb_vchans,
+			      "Configure nb_vchans not match");
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_dmadev_vchan_setup(void)
+{
+	struct rte_dmadev_vchan_conf vchan_conf = { 0 };
+	struct rte_dmadev_conf dev_conf = { 0 };
+	struct rte_dmadev_info dev_info = { 0 };
+	int ret;
+
+	/* Check for invalid parameters */
+	ret = rte_dmadev_vchan_setup(invalid_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, NULL);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Make sure configure success */
+	ret = rte_dmadev_info_get(test_dev_id, &dev_info);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to obtain device info");
+	dev_conf.nb_vchans = dev_info.max_vchans;
+	ret = rte_dmadev_configure(test_dev_id, &dev_conf);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure dmadev, %d", ret);
+
+	/* Check for invalid vchan */
+	ret = rte_dmadev_vchan_setup(test_dev_id, dev_conf.nb_vchans,
+				     &vchan_conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Check for direction */
+	memset(&vchan_conf, 0, sizeof(vchan_conf));
+	vchan_conf.direction = RTE_DMA_DIR_DEV_TO_DEV + 1;
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	vchan_conf.direction = RTE_DMA_DIR_MEM_TO_MEM - 1;
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Check for direction and dev_capa combination */
+	memset(&vchan_conf, 0, sizeof(vchan_conf));
+	vchan_conf.direction = RTE_DMA_DIR_MEM_TO_DEV;
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	vchan_conf.direction = RTE_DMA_DIR_DEV_TO_MEM;
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	vchan_conf.direction = RTE_DMA_DIR_DEV_TO_DEV;
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Check for nb_desc validation */
+	memset(&vchan_conf, 0, sizeof(vchan_conf));
+	vchan_conf.direction = RTE_DMA_DIR_MEM_TO_MEM;
+	vchan_conf.nb_desc = dev_info.min_desc - 1;
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	vchan_conf.nb_desc = dev_info.max_desc + 1;
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Check src port type validation */
+	memset(&vchan_conf, 0, sizeof(vchan_conf));
+	vchan_conf.direction = RTE_DMA_DIR_MEM_TO_MEM;
+	vchan_conf.nb_desc = dev_info.min_desc;
+	vchan_conf.src_port.port_type = RTE_DMADEV_PORT_PCIE;
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Check dst port type validation */
+	memset(&vchan_conf, 0, sizeof(vchan_conf));
+	vchan_conf.direction = RTE_DMA_DIR_MEM_TO_MEM;
+	vchan_conf.nb_desc = dev_info.min_desc;
+	vchan_conf.dst_port.port_type = RTE_DMADEV_PORT_PCIE;
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Check vchan setup success */
+	memset(&vchan_conf, 0, sizeof(vchan_conf));
+	vchan_conf.direction = RTE_DMA_DIR_MEM_TO_MEM;
+	vchan_conf.nb_desc = dev_info.min_desc;
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to setup vchan, %d", ret);
+
+	return TEST_SUCCESS;
+}
+
+static int
+setup_one_vchan(void)
+{
+	struct rte_dmadev_vchan_conf vchan_conf = { 0 };
+	struct rte_dmadev_info dev_info = { 0 };
+	struct rte_dmadev_conf dev_conf = { 0 };
+	int ret;
+
+	ret = rte_dmadev_info_get(test_dev_id, &dev_info);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to obtain device info, %d", ret);
+	dev_conf.nb_vchans = dev_info.max_vchans;
+	ret = rte_dmadev_configure(test_dev_id, &dev_conf);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to configure, %d", ret);
+	vchan_conf.direction = RTE_DMA_DIR_MEM_TO_MEM;
+	vchan_conf.nb_desc = dev_info.min_desc;
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to setup vchan, %d", ret);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_dmadev_start_stop(void)
+{
+	struct rte_dmadev_vchan_conf vchan_conf = { 0 };
+	struct rte_dmadev_conf dev_conf = { 0 };
+	int ret;
+
+	/* Check for invalid parameters */
+	ret = rte_dmadev_start(invalid_dev_id);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	ret = rte_dmadev_stop(invalid_dev_id);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Setup one vchan for later test */
+	ret = setup_one_vchan();
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to setup one vchan, %d", ret);
+
+	ret = rte_dmadev_start(test_dev_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to start, %d", ret);
+
+	/* Check reconfigure and vchan setup when device started */
+	ret = rte_dmadev_configure(test_dev_id, &dev_conf);
+	RTE_TEST_ASSERT(ret == -EBUSY, "Failed to configure, %d", ret);
+	ret = rte_dmadev_vchan_setup(test_dev_id, 0, &vchan_conf);
+	RTE_TEST_ASSERT(ret == -EBUSY, "Failed to setup vchan, %d", ret);
+
+	ret = rte_dmadev_stop(test_dev_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to stop, %d", ret);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_dmadev_stats(void)
+{
+	struct rte_dmadev_info dev_info = { 0 };
+	struct rte_dmadev_stats stats = { 0 };
+	int ret;
+
+	/* Check for invalid parameters */
+	ret = rte_dmadev_stats_get(invalid_dev_id, 0, &stats);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	ret = rte_dmadev_stats_get(invalid_dev_id, 0, NULL);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	ret = rte_dmadev_stats_reset(invalid_dev_id, 0);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Setup one vchan for later test */
+	ret = setup_one_vchan();
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to setup one vchan, %d", ret);
+
+	/* Check for invalid vchan */
+	ret = rte_dmadev_info_get(test_dev_id, &dev_info);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to obtain device info, %d", ret);
+	ret = rte_dmadev_stats_get(test_dev_id, dev_info.max_vchans, &stats);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+	ret = rte_dmadev_stats_reset(test_dev_id, dev_info.max_vchans);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected -EINVAL, %d", ret);
+
+	/* Check for valid vchan */
+	ret = rte_dmadev_stats_get(test_dev_id, 0, &stats);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get stats, %d", ret);
+	ret = rte_dmadev_stats_get(test_dev_id, RTE_DMADEV_ALL_VCHAN, &stats);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to get all stats, %d", ret);
+	ret = rte_dmadev_stats_reset(test_dev_id, 0);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to reset stats, %d", ret);
+	ret = rte_dmadev_stats_reset(test_dev_id, RTE_DMADEV_ALL_VCHAN);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to reset all stats, %d", ret);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_dmadev_completed(void)
+{
+	uint16_t last_idx = 1;
+	bool has_error = true;
+	uint16_t cpl_ret;
+	int ret, i;
+
+	/* Setup one vchan for later test */
+	ret = setup_one_vchan();
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to setup one vchan, %d", ret);
+
+	ret = rte_dmadev_start(test_dev_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to start, %d", ret);
+
+	/* Setup test memory */
+	for (i = 0; i < TEST_MEMCPY_SIZE; i++)
+		src[i] = (char)i;
+	memset(dst, 0, TEST_MEMCPY_SIZE);
+
+	/* Check enqueue without submit */
+	ret = rte_dmadev_copy(test_dev_id, 0, (rte_iova_t)src, (rte_iova_t)dst,
+				TEST_MEMCPY_SIZE, 0);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to enqueue copy, %d", ret);
+	rte_delay_us_sleep(TEST_WAIT_US_VAL);
+	cpl_ret = rte_dmadev_completed(test_dev_id, 0, 1, &last_idx,
+				       &has_error);
+	RTE_TEST_ASSERT_EQUAL(cpl_ret, 0, "Failed to get completed");
+
+	/* Check add submit */
+	ret = rte_dmadev_submit(test_dev_id, 0);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to submit, %d", ret);
+	rte_delay_us_sleep(TEST_WAIT_US_VAL);
+	cpl_ret = rte_dmadev_completed(test_dev_id, 0, 1, &last_idx,
+				       &has_error);
+	RTE_TEST_ASSERT_EQUAL(cpl_ret, 1, "Failed to get completed");
+	RTE_TEST_ASSERT_EQUAL(last_idx, 0, "Last idx should be zero, %u",
+				last_idx);
+	RTE_TEST_ASSERT_EQUAL(has_error, false, "Should have no error");
+	for (i = 0; i < TEST_MEMCPY_SIZE; i++) {
+		if (src[i] != dst[i]) {
+			RTE_TEST_ASSERT_EQUAL(src[i], dst[i],
+				"Failed to copy memory, %d %d", src[i], dst[i]);
+			break;
+		}
+	}
+
+	/* Setup test memory */
+	for (i = 0; i < TEST_MEMCPY_SIZE; i++)
+		src[i] = (char)i;
+	memset(dst, 0, TEST_MEMCPY_SIZE);
+
+	/* Check for enqueue with submit */
+	ret = rte_dmadev_copy(test_dev_id, 0, (rte_iova_t)src, (rte_iova_t)dst,
+				TEST_MEMCPY_SIZE, RTE_DMA_OP_FLAG_SUBMIT);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to enqueue copy, %d", ret);
+	rte_delay_us_sleep(TEST_WAIT_US_VAL);
+	cpl_ret = rte_dmadev_completed(test_dev_id, 0, 1, &last_idx,
+				       &has_error);
+	RTE_TEST_ASSERT_EQUAL(cpl_ret, 1, "Failed to get completed");
+	RTE_TEST_ASSERT_EQUAL(last_idx, 1, "Last idx should be 1, %u",
+				last_idx);
+	RTE_TEST_ASSERT_EQUAL(has_error, false, "Should have no error");
+	for (i = 0; i < TEST_MEMCPY_SIZE; i++) {
+		if (src[i] != dst[i]) {
+			RTE_TEST_ASSERT_EQUAL(src[i], dst[i],
+				"Failed to copy memory, %d %d", src[i], dst[i]);
+			break;
+		}
+	}
+
+	/* Stop dmadev to make sure dmadev to a known state */
+	ret = rte_dmadev_stop(test_dev_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to stop, %d", ret);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_dmadev_completed_status(void)
+{
+	enum rte_dma_status_code status[1] = { 1 };
+	uint16_t last_idx = 1;
+	uint16_t cpl_ret, i;
+	int ret;
+
+	/* Setup one vchan for later test */
+	ret = setup_one_vchan();
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to setup one vchan, %d", ret);
+
+	ret = rte_dmadev_start(test_dev_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to start, %d", ret);
+
+	/* Check for enqueue with submit */
+	ret = rte_dmadev_copy(test_dev_id, 0, (rte_iova_t)src, (rte_iova_t)dst,
+				TEST_MEMCPY_SIZE, RTE_DMA_OP_FLAG_SUBMIT);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to enqueue copy, %d", ret);
+	rte_delay_us_sleep(TEST_WAIT_US_VAL);
+	cpl_ret = rte_dmadev_completed_status(test_dev_id, 0, 1, &last_idx,
+					      status);
+	RTE_TEST_ASSERT_EQUAL(cpl_ret, 1, "Failed to completed status");
+	RTE_TEST_ASSERT_EQUAL(last_idx, 0, "Last idx should be zero, %u",
+				last_idx);
+	for (i = 0; i < RTE_DIM(status); i++)
+		RTE_TEST_ASSERT_EQUAL(status[i], 0,
+				"Failed to completed status, %d", status[i]);
+
+	/* Check do completed status again */
+	cpl_ret = rte_dmadev_completed_status(test_dev_id, 0, 1, &last_idx,
+					      status);
+	RTE_TEST_ASSERT_EQUAL(cpl_ret, 0, "Failed to completed status");
+
+	/* Check for enqueue with submit again */
+	ret = rte_dmadev_copy(test_dev_id, 0, (rte_iova_t)src, (rte_iova_t)dst,
+				TEST_MEMCPY_SIZE, RTE_DMA_OP_FLAG_SUBMIT);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to enqueue copy, %d", ret);
+	rte_delay_us_sleep(TEST_WAIT_US_VAL);
+	cpl_ret = rte_dmadev_completed_status(test_dev_id, 0, 1, &last_idx,
+					      status);
+	RTE_TEST_ASSERT_EQUAL(cpl_ret, 1, "Failed to completed status");
+	RTE_TEST_ASSERT_EQUAL(last_idx, 1, "Last idx should be 1, %u",
+				last_idx);
+	for (i = 0; i < RTE_DIM(status); i++)
+		RTE_TEST_ASSERT_EQUAL(status[i], 0,
+				"Failed to completed status, %d", status[i]);
+
+	/* Stop dmadev to make sure dmadev to a known state */
+	ret = rte_dmadev_stop(test_dev_id);
+	RTE_TEST_ASSERT_SUCCESS(ret, "Failed to stop, %d", ret);
+
+	return TEST_SUCCESS;
+}
+
+int
+test_dma_skeleton(uint16_t dev_id)
+{
+	int ret = testsuite_setup(dev_id);
+	if (ret) {
+		SKELDMA_TEST_INFO("testsuite setup fail!");
+		return -1;
+	}
+
+	/* If the testcase exit successfully, ensure that the test dmadev exist
+	 * and the dmadev is in the stopped state.
+	 */
+	SKELDMA_TEST_RUN(test_dmadev_get_dev_id);
+	SKELDMA_TEST_RUN(test_dmadev_is_valid_dev);
+	SKELDMA_TEST_RUN(test_dmadev_count);
+	SKELDMA_TEST_RUN(test_dmadev_info_get);
+	SKELDMA_TEST_RUN(test_dmadev_configure);
+	SKELDMA_TEST_RUN(test_dmadev_vchan_setup);
+	SKELDMA_TEST_RUN(test_dmadev_start_stop);
+	SKELDMA_TEST_RUN(test_dmadev_stats);
+	SKELDMA_TEST_RUN(test_dmadev_completed);
+	SKELDMA_TEST_RUN(test_dmadev_completed_status);
+
+	testsuite_teardown();
+
+	SKELDMA_TEST_INFO("Total tests   : %d\n", total);
+	SKELDMA_TEST_INFO("Passed        : %d\n", passed);
+	SKELDMA_TEST_INFO("Failed        : %d\n", failed);
+
+	if (failed)
+		return -1;
+
+	return 0;
+};