From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 20611A0C4C; Tue, 13 Jul 2021 18:02:40 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 9FF2B41257; Tue, 13 Jul 2021 18:02:39 +0200 (CEST) Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) by mails.dpdk.org (Postfix) with ESMTP id F2DB641238 for ; Tue, 13 Jul 2021 18:02:37 +0200 (CEST) X-IronPort-AV: E=McAfee;i="6200,9189,10044"; a="231994022" X-IronPort-AV: E=Sophos;i="5.84,236,1620716400"; d="scan'208";a="231994022" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Jul 2021 09:02:35 -0700 X-IronPort-AV: E=Sophos;i="5.84,236,1620716400"; d="scan'208";a="570506042" Received: from fhogg-mobl1.ger.corp.intel.com (HELO bricha3-MOBL.ger.corp.intel.com) ([10.252.27.49]) by fmsmga001-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-SHA; 13 Jul 2021 09:02:32 -0700 Date: Tue, 13 Jul 2021 17:02:28 +0100 From: Bruce Richardson To: Chengwen Feng Cc: thomas@monjalon.net, ferruh.yigit@intel.com, jerinj@marvell.com, jerinjacobk@gmail.com, andrew.rybchenko@oktetlabs.ru, dev@dpdk.org, mb@smartsharesystems.com, nipun.gupta@nxp.com, hemant.agrawal@nxp.com, maxime.coquelin@redhat.com, honnappa.nagarahalli@arm.com, david.marchand@redhat.com, sburla@marvell.com, pkapoor@marvell.com, konstantin.ananyev@intel.com Message-ID: References: <1625231891-2963-1-git-send-email-fengchengwen@huawei.com> <1626179263-14645-1-git-send-email-fengchengwen@huawei.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <1626179263-14645-1-git-send-email-fengchengwen@huawei.com> Subject: Re: [dpdk-dev] [PATCH v3] dmadev: introduce DMA device library 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" On Tue, Jul 13, 2021 at 08:27:43PM +0800, Chengwen Feng wrote: > This patch introduce 'dmadevice' which is a generic type of DMA > device. > > The APIs of dmadev library exposes some generic operations which can > enable configuration and I/O with the DMA devices. > > Signed-off-by: Chengwen Feng > --- > v3: > * rm reset and fill_sg ops. > * rm MT-safe capabilities. > * add submit flag. > * redefine rte_dma_sg to implement asymmetric copy. > * delete some reserved field for future use. > * rearrangement rte_dmadev/rte_dmadev_data struct. > * refresh rte_dmadev.h copyright. > * update vchan setup parameter. > * modified some inappropriate descriptions. > * arrange version.map alphabetically. > * other minor modifications from review comment. > --- Thanks, some further comments inline below on the .c file initially. /Bruce > MAINTAINERS | 4 + > config/rte_config.h | 3 + > lib/dmadev/meson.build | 7 + > lib/dmadev/rte_dmadev.c | 561 +++++++++++++++++++++++++ > lib/dmadev/rte_dmadev.h | 968 +++++++++++++++++++++++++++++++++++++++++++ > lib/dmadev/rte_dmadev_core.h | 161 +++++++ > lib/dmadev/rte_dmadev_pmd.h | 72 ++++ > lib/dmadev/version.map | 37 ++ > lib/meson.build | 1 + > 9 files changed, 1814 insertions(+) > create mode 100644 lib/dmadev/meson.build > create mode 100644 lib/dmadev/rte_dmadev.c > create mode 100644 lib/dmadev/rte_dmadev.h > create mode 100644 lib/dmadev/rte_dmadev_core.h > create mode 100644 lib/dmadev/rte_dmadev_pmd.h > create mode 100644 lib/dmadev/version.map > > diff --git a/MAINTAINERS b/MAINTAINERS > index af2a91d..e01a07f 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -495,6 +495,10 @@ F: drivers/raw/skeleton/ > F: app/test/test_rawdev.c > F: doc/guides/prog_guide/rawdev.rst > > +DMA device API - EXPERIMENTAL > +M: Chengwen Feng > +F: lib/dmadev/ > + > > Memory Pool Drivers > ------------------- > diff --git a/config/rte_config.h b/config/rte_config.h > index 590903c..331a431 100644 > --- a/config/rte_config.h > +++ b/config/rte_config.h > @@ -81,6 +81,9 @@ > /* rawdev defines */ > #define RTE_RAWDEV_MAX_DEVS 64 > > +/* dmadev defines */ > +#define RTE_DMADEV_MAX_DEVS 64 > + > /* ip_fragmentation defines */ > #define RTE_LIBRTE_IP_FRAG_MAX_FRAG 4 > #undef RTE_LIBRTE_IP_FRAG_TBL_STAT > diff --git a/lib/dmadev/meson.build b/lib/dmadev/meson.build > new file mode 100644 > index 0000000..d2fc85e > --- /dev/null > +++ b/lib/dmadev/meson.build > @@ -0,0 +1,7 @@ > +# SPDX-License-Identifier: BSD-3-Clause > +# Copyright(c) 2021 HiSilicon Limited. > + > +sources = files('rte_dmadev.c') > +headers = files('rte_dmadev.h') > +indirect_headers += files('rte_dmadev_core.h') > +driver_sdk_headers += files('rte_dmadev_pmd.h') > diff --git a/lib/dmadev/rte_dmadev.c b/lib/dmadev/rte_dmadev.c > new file mode 100644 > index 0000000..1bca463 > --- /dev/null > +++ b/lib/dmadev/rte_dmadev.c > @@ -0,0 +1,561 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright(c) 2021 HiSilicon Limited. > + * Copyright(c) 2021 Intel Corporation. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "rte_dmadev.h" > +#include "rte_dmadev_pmd.h" > + > +struct rte_dmadev rte_dmadevices[RTE_DMADEV_MAX_DEVS]; > + > +static const char *MZ_RTE_DMADEV_DATA = "rte_dmadev_data"; > +/* Shared memory between primary and secondary processes. */ > +static struct { > + struct rte_dmadev_data data[RTE_DMADEV_MAX_DEVS]; > +} *dmadev_shared_data; > + > +RTE_LOG_REGISTER(rte_dmadev_logtype, lib.dmadev, INFO); There is an RTE_LOG_REGISTER_DEFAULT macro which can be used here instead. Also, since the logtype is not exposed outside this file, we can drop the prefix on it to shorten it: "RTE_LOG_REGISTER_DEFAULT(logtype, INFO);" > +#define RTE_DMADEV_LOG(level, ...) \ > + rte_log(RTE_LOG_ ## level, rte_dmadev_logtype, "" __VA_ARGS__) > + > +/* Macros to check for valid device id */ > +#define RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, retval) do { \ > + if (!rte_dmadev_is_valid_dev(dev_id)) { \ > + RTE_DMADEV_LOG(ERR, "Invalid dev_id=%u\n", dev_id); \ > + return retval; \ > + } \ > +} while (0) > + > +#define RTE_DMADEV_VALID_DEV_ID_OR_RET(dev_id) do { \ > + if (!rte_dmadev_is_valid_dev(dev_id)) { \ > + RTE_DMADEV_LOG(ERR, "Invalid dev_id=%u\n", dev_id); \ > + return; \ > + } \ > +} while (0) > + Looking through the code, this macro appears unused, since all functions return values. The former "RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET" can also be shorted to remove prefixes, because it's again local to the file. Suggest: "VALID_DEV_ID_OR_ERR" > +/* Macro to check for invalid pointers */ > +#define RTE_DMADEV_PTR_OR_ERR_RET(ptr, retval) do { \ > + if ((ptr) == NULL) \ > + return retval; \ > +} while (0) > + This is a very short macro, so in practice it's only saving one line of code. Also, with current use, the "retval" is always -EINVAL. I'd tend towards dropping the macro, but if we want one, I'd suggest a short one-line one: "#define CHECK_PTR_PARAM(ptr) if ((ptr) == NULL) return -EINVAL" However, overall I don't think it's worth it - case in point, see the check for "name" below which skips using the macro anyway. > +static int > +dmadev_check_name(const char *name) > +{ > + size_t name_len; > + > + if (name == NULL) { > + RTE_DMADEV_LOG(ERR, "Name can't be NULL\n"); > + return -EINVAL; > + } > + > + name_len = strnlen(name, RTE_DMADEV_NAME_MAX_LEN); > + if (name_len == 0) { > + RTE_DMADEV_LOG(ERR, "Zero length DMA device name\n"); > + return -EINVAL; > + } > + if (name_len >= RTE_DMADEV_NAME_MAX_LEN) { > + RTE_DMADEV_LOG(ERR, "DMA device name is too long\n"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static uint16_t > +dmadev_find_free_dev(void) > +{ > + uint16_t i; > + > + for (i = 0; i < RTE_DMADEV_MAX_DEVS; i++) { > + if (dmadev_shared_data->data[i].dev_name[0] == '\0') { > + RTE_ASSERT(rte_dmadevices[i].state == > + RTE_DMADEV_UNUSED); > + return i; > + } > + } > + > + return RTE_DMADEV_MAX_DEVS; > +} > + > +static struct rte_dmadev* > +dmadev_find(const char *name) > +{ > + uint16_t i; > + > + for (i = 0; i < RTE_DMADEV_MAX_DEVS; i++) { > + if ((rte_dmadevices[i].state == RTE_DMADEV_ATTACHED) && > + (!strcmp(name, rte_dmadevices[i].data->dev_name))) > + return &rte_dmadevices[i]; > + } > + > + return NULL; > +} > + > +static int > +dmadev_shared_data_prepare(void) > +{ > + const struct rte_memzone *mz; > + > + if (dmadev_shared_data == NULL) { > + if (rte_eal_process_type() == RTE_PROC_PRIMARY) { > + /* Allocate port data and ownership shared memory. */ > + mz = rte_memzone_reserve(MZ_RTE_DMADEV_DATA, > + sizeof(*dmadev_shared_data), > + rte_socket_id(), 0); > + } else { > + mz = rte_memzone_lookup(MZ_RTE_DMADEV_DATA); > + } Minor nit, our coding style for DPDK says to omit the braces around single-statement legs like this. > + if (mz == NULL) > + return -ENOMEM; > + > + dmadev_shared_data = mz->addr; > + if (rte_eal_process_type() == RTE_PROC_PRIMARY) > + memset(dmadev_shared_data->data, 0, > + sizeof(dmadev_shared_data->data)); I believe all memzones are zero on allocation anyway, so this memset is unecessary and can be dropped. > + } > + > + return 0; > +} > + > +static struct rte_dmadev * > +dmadev_allocate(const char *name) > +{ > + struct rte_dmadev *dev; > + uint16_t dev_id; > + > + dev = dmadev_find(name); > + if (dev != NULL) { > + RTE_DMADEV_LOG(ERR, "DMA device already allocated\n"); > + return NULL; > + } > + > + dev_id = dmadev_find_free_dev(); > + if (dev_id == RTE_DMADEV_MAX_DEVS) { > + RTE_DMADEV_LOG(ERR, "Reached maximum number of DMA devices\n"); > + return NULL; > + } > + > + if (dmadev_shared_data_prepare() != 0) { > + RTE_DMADEV_LOG(ERR, "Cannot allocate DMA shared data\n"); > + return NULL; > + } > + > + dev = &rte_dmadevices[dev_id]; > + dev->data = &dmadev_shared_data->data[dev_id]; > + dev->data->dev_id = dev_id; > + strlcpy(dev->data->dev_name, name, sizeof(dev->data->dev_name)); > + > + return dev; > +} > + > +static struct rte_dmadev * > +dmadev_attach_secondary(const char *name) > +{ > + struct rte_dmadev *dev; > + uint16_t i; > + > + if (dmadev_shared_data_prepare() != 0) { > + RTE_DMADEV_LOG(ERR, "Cannot allocate DMA shared data\n"); > + return NULL; > + } > + > + for (i = 0; i < RTE_DMADEV_MAX_DEVS; i++) { > + if (!strcmp(dmadev_shared_data->data[i].dev_name, name)) > + break; > + } > + if (i == RTE_DMADEV_MAX_DEVS) { > + RTE_DMADEV_LOG(ERR, > + "Device %s is not driven by the primary process\n", > + name); > + return NULL; > + } > + > + dev = &rte_dmadevices[i]; > + dev->data = &dmadev_shared_data->data[i]; > + RTE_ASSERT(dev->data->dev_id == i); > + > + return dev; > +} > + > +struct rte_dmadev * > +rte_dmadev_pmd_allocate(const char *name) > +{ > + struct rte_dmadev *dev; > + > + if (dmadev_check_name(name) != 0) > + return NULL; > + > + if (rte_eal_process_type() == RTE_PROC_PRIMARY) > + dev = dmadev_allocate(name); > + else > + dev = dmadev_attach_secondary(name); > + > + if (dev == NULL) > + return NULL; > + dev->state = RTE_DMADEV_ATTACHED; > + > + return dev; > +} > + > +int > +rte_dmadev_pmd_release(struct rte_dmadev *dev) > +{ > + if (dev == NULL) > + return -EINVAL; > + > + if (dev->state == RTE_DMADEV_UNUSED) > + return 0; > + > + if (rte_eal_process_type() == RTE_PROC_PRIMARY) { > + rte_free(dev->data->dev_private); There seems an imbalance here. If we "free" on release, we should similarly "malloc" on allocate, otherwise we run the risk of dev_private being allocated using regular malloc in a driver, for example. I think some other allocation APIs take as parameter the private data size to reserve, and we can follow that model. > + memset(dev->data, 0, sizeof(struct rte_dmadev_data)); > + } > + > + memset(dev, 0, sizeof(struct rte_dmadev)); > + dev->state = RTE_DMADEV_UNUSED; > + > + return 0; > +} > + > +struct rte_dmadev * > +rte_dmadev_get_device_by_name(const char *name) > +{ > + if (dmadev_check_name(name) != 0) > + return NULL; > + return dmadev_find(name); > +} > + > +bool > +rte_dmadev_is_valid_dev(uint16_t dev_id) > +{ > + if (dev_id >= RTE_DMADEV_MAX_DEVS || > + rte_dmadevices[dev_id].state != RTE_DMADEV_ATTACHED) > + return false; > + return true; > +} Can be a one-line function: "return (dev_id < RTE_DMADEV_MAX_DEVS && rte_dmadevices[dev_id].state == RTE_DMADEV_ATTACHED);" > + > +uint16_t > +rte_dmadev_count(void) > +{ > + uint16_t count = 0; > + uint16_t i; > + > + for (i = 0; i < RTE_DMADEV_MAX_DEVS; i++) { > + if (rte_dmadevices[i].state == RTE_DMADEV_ATTACHED) > + count++; > + } > + > + return count; > +} > + > +int > +rte_dmadev_info_get(uint16_t dev_id, struct rte_dmadev_info *dev_info) > +{ > + const struct rte_dmadev *dev; > + int ret; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + RTE_DMADEV_PTR_OR_ERR_RET(dev_info, -EINVAL); > + > + dev = &rte_dmadevices[dev_id]; This line can be merged into the definition of dev, since it's just assigning an address and never referencing it. > + > + RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_info_get, -ENOTSUP); > + memset(dev_info, 0, sizeof(struct rte_dmadev_info)); > + ret = (*dev->dev_ops->dev_info_get)(dev, dev_info, > + sizeof(struct rte_dmadev_info)); > + if (ret != 0) > + return ret; > + > + dev_info->device = dev->device; > + dev_info->nb_vchans = dev->data->dev_conf.max_vchans; > + > + return 0; > +} > + > +int > +rte_dmadev_configure(uint16_t dev_id, const struct rte_dmadev_conf *dev_conf) > +{ > + struct rte_dmadev_info info; > + struct rte_dmadev *dev; > + int ret; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + RTE_DMADEV_PTR_OR_ERR_RET(dev_conf, -EINVAL); > + dev = &rte_dmadevices[dev_id]; As above, merge into definition line of dev as: "struct rte_dmadev *dev = &rte_dmadevices[dev_id];" > + > + ret = rte_dmadev_info_get(dev_id, &info); > + if (ret != 0) { > + RTE_DMADEV_LOG(ERR, "Device %u get device info fail\n", dev_id); > + return -EINVAL; > + } > + if (dev_conf->max_vchans > info.max_vchans) { > + RTE_DMADEV_LOG(ERR, > + "Device %u configure too many vchans\n", dev_id); > + return -EINVAL; > + } > + > + if (dev->data->dev_started != 0) { > + RTE_DMADEV_LOG(ERR, > + "Device %u must be stopped to allow configuration\n", > + dev_id); > + return -EBUSY; > + } > + > + RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_configure, -ENOTSUP); Rather than putting in all these checks and returning -ENOTSUP, I'd like propose that we instead have the ops structure assigned as part of "rte_dmadev_pmd_allocate()" function. That then allows us to enforce that each device supports the minimum set of functions, i.e. info_get, configure, etc. etc. > + ret = (*dev->dev_ops->dev_configure)(dev, dev_conf); > + if (ret == 0) > + memcpy(&dev->data->dev_conf, dev_conf, sizeof(*dev_conf)); > + > + return ret; > +} > + > +int > +rte_dmadev_start(uint16_t dev_id) > +{ > + struct rte_dmadev *dev; > + int ret; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + dev = &rte_dmadevices[dev_id]; > + > + if (dev->data->dev_started != 0) { > + RTE_DMADEV_LOG(WARNING, "Device %u already started\n", dev_id); > + return 0; > + } > + > + if (dev->dev_ops->dev_start == NULL) > + goto mark_started; > + > + ret = (*dev->dev_ops->dev_start)(dev); > + if (ret != 0) > + return ret; > + > +mark_started: > + dev->data->dev_started = 1; > + return 0; > +} > + > +int > +rte_dmadev_stop(uint16_t dev_id) > +{ > + struct rte_dmadev *dev; > + int ret; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + dev = &rte_dmadevices[dev_id]; > + > + if (dev->data->dev_started == 0) { > + RTE_DMADEV_LOG(WARNING, "Device %u already stopped\n", dev_id); > + return 0; > + } > + > + if (dev->dev_ops->dev_stop == NULL) > + goto mark_stopped; > + > + ret = (*dev->dev_ops->dev_stop)(dev); > + if (ret != 0) > + return ret; > + > +mark_stopped: > + dev->data->dev_started = 0; > + return 0; > +} > + > +int > +rte_dmadev_close(uint16_t dev_id) > +{ > + struct rte_dmadev *dev; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + dev = &rte_dmadevices[dev_id]; > + > + /* Device must be stopped before it can be closed */ > + if (dev->data->dev_started == 1) { > + RTE_DMADEV_LOG(ERR, > + "Device %u must be stopped before closing\n", dev_id); > + return -EBUSY; > + } > + > + RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_close, -ENOTSUP); > + return (*dev->dev_ops->dev_close)(dev); > +} > + > +int > +rte_dmadev_vchan_setup(uint16_t dev_id, > + const struct rte_dmadev_vchan_conf *conf) > +{ > + struct rte_dmadev_info info; > + struct rte_dmadev *dev; > + int ret; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + RTE_DMADEV_PTR_OR_ERR_RET(conf, -EINVAL); > + > + dev = &rte_dmadevices[dev_id]; > + > + ret = rte_dmadev_info_get(dev_id, &info); > + if (ret != 0) { > + RTE_DMADEV_LOG(ERR, "Device %u get device info fail\n", dev_id); > + return -EINVAL; > + } > + if (conf->direction == 0 || > + conf->direction & ~RTE_DMA_TRANSFER_DIR_ALL) { > + RTE_DMADEV_LOG(ERR, "Device %u direction invalid!\n", dev_id); > + return -EINVAL; > + } > + if (conf->direction & RTE_DMA_MEM_TO_MEM && > + !(info.dev_capa & RTE_DMA_DEV_CAPA_MEM_TO_MEM)) { > + RTE_DMADEV_LOG(ERR, > + "Device %u don't support mem2mem transfer\n", dev_id); > + return -EINVAL; > + } > + if (conf->direction & RTE_DMA_MEM_TO_DEV && > + !(info.dev_capa & RTE_DMA_DEV_CAPA_MEM_TO_DEV)) { > + RTE_DMADEV_LOG(ERR, > + "Device %u don't support mem2dev transfer\n", dev_id); > + return -EINVAL; > + } > + if (conf->direction & RTE_DMA_DEV_TO_MEM && > + !(info.dev_capa & RTE_DMA_DEV_CAPA_DEV_TO_MEM)) { > + RTE_DMADEV_LOG(ERR, > + "Device %u don't support dev2mem transfer\n", dev_id); > + return -EINVAL; > + } > + if (conf->direction & RTE_DMA_DEV_TO_DEV && > + !(info.dev_capa & RTE_DMA_DEV_CAPA_DEV_TO_DEV)) { > + RTE_DMADEV_LOG(ERR, > + "Device %u don't support dev2dev transfer\n", dev_id); > + return -EINVAL; > + } Rather than checking each one of these individually, can we just merge these checks into one? > + if (conf->nb_desc < info.min_desc || conf->nb_desc > info.max_desc) { > + RTE_DMADEV_LOG(ERR, > + "Device %u number of descriptors invalid\n", dev_id); > + return -EINVAL; > + } > + > + RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->vchan_setup, -ENOTSUP); > + return (*dev->dev_ops->vchan_setup)(dev, conf); > +} > + > +int > +rte_dmadev_vchan_release(uint16_t dev_id, uint16_t vchan) > +{ > + struct rte_dmadev *dev; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + dev = &rte_dmadevices[dev_id]; > + > + if (vchan >= dev->data->dev_conf.max_vchans) { > + RTE_DMADEV_LOG(ERR, > + "Device %u vchan %u out of range\n", dev_id, vchan); > + return -EINVAL; > + } > + > + RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->vchan_release, -ENOTSUP); > + return (*dev->dev_ops->vchan_release)(dev, vchan); > +} > + > +int > +rte_dmadev_stats_get(uint16_t dev_id, uint16_t vchan, > + struct rte_dmadev_stats *stats) > +{ > + const struct rte_dmadev *dev; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + RTE_DMADEV_PTR_OR_ERR_RET(stats, -EINVAL); > + > + dev = &rte_dmadevices[dev_id]; > + > + if (vchan >= dev->data->dev_conf.max_vchans && > + vchan != RTE_DMADEV_ALL_VCHAN) { > + RTE_DMADEV_LOG(ERR, > + "Device %u vchan %u out of range\n", dev_id, vchan); > + return -EINVAL; > + } > + > + RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->stats_get, -ENOTSUP); > + return (*dev->dev_ops->stats_get)(dev, vchan, stats, > + sizeof(struct rte_dmadev_stats)); > +} > + > +int > +rte_dmadev_stats_reset(uint16_t dev_id, uint16_t vchan) > +{ > + struct rte_dmadev *dev; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + dev = &rte_dmadevices[dev_id]; > + > + if (vchan >= dev->data->dev_conf.max_vchans && > + vchan != RTE_DMADEV_ALL_VCHAN) { > + RTE_DMADEV_LOG(ERR, > + "Device %u vchan %u out of range\n", dev_id, vchan); > + return -EINVAL; > + } > + > + RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->stats_reset, -ENOTSUP); > + return (*dev->dev_ops->stats_reset)(dev, vchan); > +} > + > +int > +rte_dmadev_dump(uint16_t dev_id, FILE *f) > +{ > + const struct rte_dmadev *dev; > + struct rte_dmadev_info info; > + int ret; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + RTE_DMADEV_PTR_OR_ERR_RET(f, -EINVAL); > + > + ret = rte_dmadev_info_get(dev_id, &info); > + if (ret != 0) { > + RTE_DMADEV_LOG(ERR, "Device %u get device info fail\n", dev_id); > + return -EINVAL; > + } > + > + dev = &rte_dmadevices[dev_id]; > + > + fprintf(f, "DMA Dev %u, '%s' [%s]\n", > + dev->data->dev_id, > + dev->data->dev_name, > + dev->data->dev_started ? "started" : "stopped"); > + fprintf(f, " dev_capa: 0x%" PRIx64 "\n", info.dev_capa); > + fprintf(f, " max_vchans_supported: %u\n", info.max_vchans); > + fprintf(f, " max_vchans_configured: %u\n", info.nb_vchans); > + > + if (dev->dev_ops->dev_dump != NULL) > + return (*dev->dev_ops->dev_dump)(dev, f); > + > + return 0; > +} > + > +int > +rte_dmadev_selftest(uint16_t dev_id) > +{ > + struct rte_dmadev *dev; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + dev = &rte_dmadevices[dev_id]; > + > + RTE_FUNC_PTR_OR_ERR_RET(*dev->dev_ops->dev_selftest, -ENOTSUP); > + return (*dev->dev_ops->dev_selftest)(dev_id); > +}