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 5A154A0C4C; Mon, 12 Jul 2021 14:05:55 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id CF0F14069D; Mon, 12 Jul 2021 14:05:54 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by mails.dpdk.org (Postfix) with ESMTP id CFD8940685 for ; Mon, 12 Jul 2021 14:05:52 +0200 (CEST) X-IronPort-AV: E=McAfee;i="6200,9189,10042"; a="197240321" X-IronPort-AV: E=Sophos;i="5.84,232,1620716400"; d="scan'208";a="197240321" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Jul 2021 05:05:51 -0700 X-IronPort-AV: E=Sophos;i="5.84,232,1620716400"; d="scan'208";a="569470377" Received: from bricha3-mobl.ger.corp.intel.com ([10.252.5.145]) by fmsmga001-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-SHA; 12 Jul 2021 05:05:47 -0700 Date: Mon, 12 Jul 2021 13:05:44 +0100 From: Bruce Richardson To: Chengwen Feng Cc: thomas@monjalon.net, ferruh.yigit@intel.com, jerinj@marvell.com, jerinjacobk@gmail.com, 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, liangma@liangbit.com Message-ID: References: <1625231891-2963-1-git-send-email-fengchengwen@huawei.com> <1625995556-41473-1-git-send-email-fengchengwen@huawei.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <1625995556-41473-1-git-send-email-fengchengwen@huawei.com> Subject: Re: [dpdk-dev] [PATCH v2] 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 Sun, Jul 11, 2021 at 05:25:56PM +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 Thanks for this V2. Some initial (mostly minor) comments on the meson.build and dmadev .c file below. I'll review the headers in a separate email. /Bruce > --- > MAINTAINERS | 4 + > config/rte_config.h | 3 + > lib/dmadev/meson.build | 6 + > lib/dmadev/rte_dmadev.c | 560 +++++++++++++++++++++++ > lib/dmadev/rte_dmadev.h | 1030 ++++++++++++++++++++++++++++++++++++++++++ > lib/dmadev/rte_dmadev_core.h | 159 +++++++ > lib/dmadev/rte_dmadev_pmd.h | 72 +++ > lib/dmadev/version.map | 40 ++ > lib/meson.build | 1 + > 9 files changed, 1875 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 4347555..0595239 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -496,6 +496,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..c918dae > --- /dev/null > +++ b/lib/dmadev/meson.build > @@ -0,0 +1,6 @@ > +# SPDX-License-Identifier: BSD-3-Clause > +# Copyright(c) 2021 HiSilicon Limited. > + > +sources = files('rte_dmadev.c') > +headers = files('rte_dmadev.h', 'rte_dmadev_pmd.h') If rte_dmadev_pmd.h is only for PMD use, then it should be in "driver_sdk_headers". > +indirect_headers += files('rte_dmadev_core.h') > diff --git a/lib/dmadev/rte_dmadev.c b/lib/dmadev/rte_dmadev.c > new file mode 100644 > index 0000000..8a29abb > --- /dev/null > +++ b/lib/dmadev/rte_dmadev.c > @@ -0,0 +1,560 @@ > +/* 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" > + > +RTE_LOG_REGISTER(rte_dmadev_logtype, lib.dmadev, INFO); > + > +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; > + > +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].attached == 0); > + return i; > + } > + } > + > + return RTE_DMADEV_MAX_DEVS; > +} > + > +static struct rte_dmadev* > +dmadev_allocated(const char *name) The name implies a boolean lookup for whether a particular dmadev has been allocated or not. Since this returns a pointer, I think a name like "dmadev_find" or "dmadev_get" would be more appropriate. > +{ > + uint16_t i; > + > + for (i = 0; i < RTE_DMADEV_MAX_DEVS; i++) { > + if ((rte_dmadevices[i].attached == 1) && > + (!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); > + } > + 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)); > + } > + > + return 0; > +} > + > +static struct rte_dmadev * > +dmadev_allocate(const char *name) > +{ > + struct rte_dmadev *dev; > + uint16_t dev_id; > + > + dev = dmadev_allocated(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->attached = 1; > + > + return dev; > +} > + > +int > +rte_dmadev_pmd_release(struct rte_dmadev *dev) > +{ > + if (dev == NULL) > + return -EINVAL; > + > + if (dev->attached == 0) > + return 0; > + > + if (rte_eal_process_type() == RTE_PROC_PRIMARY) { > + rte_free(dev->data->dev_private); > + memset(dev->data, 0, sizeof(struct rte_dmadev_data)); > + } > + > + memset(dev, 0, sizeof(struct rte_dmadev)); > + dev->attached = 0; > + > + return 0; > +} > + > +struct rte_dmadev * > +rte_dmadev_get_device_by_name(const char *name) > +{ > + if (dmadev_check_name(name) != 0) > + return NULL; > + return dmadev_allocated(name); > +} > + > +bool > +rte_dmadev_is_valid_dev(uint16_t dev_id) > +{ > + if (dev_id >= RTE_DMADEV_MAX_DEVS || > + rte_dmadevices[dev_id].attached == 0) > + return false; > + return true; > +} > + > +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].attached == 1) > + count++; > + } > + > + return count; > +} > + > +int > +rte_dmadev_info_get(uint16_t dev_id, struct rte_dmadev_info *dev_info) > +{ > + struct rte_dmadev *dev; > + int ret; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + RTE_FUNC_PTR_OR_ERR_RET(dev_info, -EINVAL); > + > + dev = &rte_dmadevices[dev_id]; > + > + 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); > + if (ret != 0) > + return ret; > + > + dev_info->device = dev->device; > + > + return 0; > +} Should the info_get function (and the related info structure), not include in it the parameters passed into the configure function. That way, the user can query a previously set up configuration. This should be done at the dmadev level, rather than driver level, since I see the parameters are already being saved in configure below. Also, for ABI purposes, I would strongly suggest passing "sizeof(dev_info)" to the driver in the "dev_info_get" call. When dev_info changes, we can version rte_dmadev_info_get, but can't version the functions that it calls in turn. When we add a new field to the struct, the driver functions that choose to use that new field can check the size of the struct passed to determine if it's safe to write that new field or not. [So long as field is added at the end, driver functions not updated for the new field, need no changes] > + > +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_FUNC_PTR_OR_ERR_RET(dev_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 (dev_conf->max_vchans > info.max_vchans) { > + RTE_DMADEV_LOG(ERR, > + "Device %u configure too many vchans\n", dev_id); We allow up to 100 characters per line for DPDK code, so these don't need to be wrapped so aggressively. > + return -EINVAL; > + } > + if (dev_conf->enable_mt_vchan && > + !(info.dev_capa & RTE_DMA_DEV_CAPA_MT_VCHAN)) { > + RTE_DMADEV_LOG(ERR, > + "Device %u don't support MT-safe vchan\n", dev_id); > + return -EINVAL; > + } > + if (dev_conf->enable_mt_multi_vchan && > + !(info.dev_capa & RTE_DMA_DEV_CAPA_MT_MULTI_VCHAN)) { > + RTE_DMADEV_LOG(ERR, > + "Device %u don't support MT-safe multiple vchan\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); > + 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(ERR, "Device %u already started\n", dev_id); Maybe make this a warning rather than error. > + 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(ERR, "Device %u already stopped\n", dev_id); As above, suggest just warning rather than error. > + 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_reset(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_reset, -ENOTSUP); > + /* Reset is not dependent on state of the device */ > + return (*dev->dev_ops->dev_reset)(dev); > +} I would tend to agree with the query as to whether this is needed or not. Can we perhaps remove for now, and add it back later if it does prove to be needed. The less code to review and work with for the first version, the better IMHO. :-) > + > +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_FUNC_PTR_OR_ERR_RET(conf, -EINVAL); This is confusing, because you are actually doing a parameter check using a macro named for checking a function. Better to explicitly just check conf for null. > + > + 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; > + } I wonder should we allow direction == 0, to be the same as all bits set, or to be all supported bits set? > + 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; > + } > + 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, int vchan, struct rte_dmadev_stats *stats) > +{ > + struct rte_dmadev *dev; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + RTE_FUNC_PTR_OR_ERR_RET(stats, -EINVAL); > + > + dev = &rte_dmadevices[dev_id]; > + > + if (vchan < -1 || 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->stats_get, -ENOTSUP); > + return (*dev->dev_ops->stats_get)(dev, vchan, stats); > +} > + > +int > +rte_dmadev_stats_reset(uint16_t dev_id, int vchan) > +{ > + struct rte_dmadev *dev; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + dev = &rte_dmadevices[dev_id]; > + > + if (vchan < -1 || 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->stats_reset, -ENOTSUP); > + return (*dev->dev_ops->stats_reset)(dev, vchan); > +} > + > +int > +rte_dmadev_dump(uint16_t dev_id, FILE *f) > +{ > + struct rte_dmadev_info info; > + struct rte_dmadev *dev; > + int ret; > + > + RTE_DMADEV_VALID_DEV_ID_OR_ERR_RET(dev_id, -EINVAL); > + RTE_FUNC_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); > + fprintf(f, " MT-safe-configured: vchans: %u multi-vchans: %u\n", > + dev->data->dev_conf.enable_mt_vchan, > + dev->data->dev_conf.enable_mt_multi_vchan); > + > + 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); > +}