diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f0e726cc..90ba6c5b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## v21.10: (Upcoming Release) +### dma + +A new library, lib/dma, has been added. This library provides the necessary infrastructure for +handling systems and devices with multiple memory domains. For example, a PCIe add-in card with an +SoC may be running SPDK on the SoC. That SoC has its own local memory, but SPDK may be controlling +devices that can also access the host system memory. This library provides infrastructure to enumerate +the memory domains and request hardware perform DMA transfers between them. + ### log Added API `spdk_log_to_syslog_level` to return syslog level based on SPDK's diff --git a/include/spdk/dma.h b/include/spdk/dma.h new file mode 100644 index 000000000..ac64e5a58 --- /dev/null +++ b/include/spdk/dma.h @@ -0,0 +1,282 @@ +/*- + * BSD LICENSE + * + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Nvidia Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef SPDK_DMA_H +#define SPDK_DMA_H + +#include "spdk/assert.h" +#include "spdk/queue.h" +#include "spdk/stdinc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Identifier of SPDK internal DMA device of RDMA type + */ +#define SPDK_RDMA_DMA_DEVICE "SPDK_RDMA_DMA_DEVICE" + +enum spdk_dma_device_type { + /** RDMA devices are capable of performing DMA operations on memory domains using the standard + * RDMA model (protection domain, remote key, address). */ + SPDK_DMA_DEVICE_TYPE_RDMA, + /** DMA devices are capable of performing DMA operations on memory domains using physical or + * I/O virtual addresses. */ + SPDK_DMA_DEVICE_TYPE_DMA +}; + +struct spdk_memory_domain; + +/** + * Definition of completion callback to be called by fetch function. + * + * \param ctx User context passed to fetch function + * \param iov Pointer to iovs that hold result of fetch function + * \param iovcnt iov array size + * \param rc Result of asynchronous fetch function. Data in iov is valid only when rc is 0 + */ +typedef void (*spdk_memory_domain_fetch_data_cpl_cb)(void *ctx, void *iov, uint32_t iovcnt, int rc); + +/** + * Definition of function which asynchronously fetches data from src_domain to local memory domain. + * Implementation of this function must call \b cpl_cb only when it returns 0. All other return codes mean failure. + * + * \param src_domain Memory domain to which the data buffer belongs + * \param src_domain_ctx Optional context passed by upper layer with IO request + * \param src_iov Iov vector in \b src_domain space + * \param src_iovcnt src_iov array size + * \param dst_iov Iov vector in local memory domain space, data buffers must be allocated by the caller of + * this function, total size of data buffers must not be less than the size of data in \b src_iov. + * \param dst_iovcnt dst_iov array size + * \param cpl_cb A callback to be called when fetch operation completes + * \param cpl_cb_arg Optional argument to be passed to \b cpl_cb + * \return 0 on success, negated errno on failure + */ +typedef int (*spdk_memory_domain_fetch_data_cb)(struct spdk_memory_domain *src_domain, + void *src_domain_ctx, + struct iovec *src_iov, uint32_t src_iovcnt, struct iovec *dst_iov, uint32_t dst_iovcnt, + spdk_memory_domain_fetch_data_cpl_cb cpl_cb, void *cpl_cb_arg); + +struct spdk_memory_domain_translation_result { + /** size of this structure in bytes */ + size_t size; + /** Address of data buffer translated into destination memory domain space */ + void *addr; + /** Size of the data buffer */ + size_t len; + /** Destination domain passed to translation function */ + struct spdk_memory_domain *dst_domain; + union { + struct { + uint32_t lkey; + uint32_t rkey; + } rdma; + }; +}; + +struct spdk_memory_domain_translation_ctx { + /** size of this structure in bytes */ + size_t size; + union { + struct { + /* Opaque handle for ibv_qp */ + void *ibv_qp; + } rdma; + }; +}; + +/** + * Definition of function which translates data from src_domain to a form accessible by dst_domain. + * + * \param src_domain Memory domain to which the data buffer belongs + * \param src_domain_ctx Optional context passed by upper layer with IO request + * \param dst_domain Memory domain which determines type of translation function + * \param dst_domain_ctx Ancillary data for dst_domain + * \param addr Data buffer address in \b src_domain memory space which should be translated into \b dst_domain + * \param len Length of data buffer + * \param result Result of translation function + * \return 0 on success, negated errno on failure + */ +typedef int (*spdk_memory_domain_translate_memory_cb)(struct spdk_memory_domain *src_domain, + void *src_domain_ctx, struct spdk_memory_domain *dst_domain, + struct spdk_memory_domain_translation_ctx *dst_domain_ctx, void *addr, size_t len, + struct spdk_memory_domain_translation_result *result); + +struct spdk_memory_domain_ctx { + /** size of this structure in bytes */ + size_t size; + union { + struct { + /* Opaque handle for ibv_pd */ + void *ibv_pd; + } rdma; + }; +}; + +/** + * Creates a new memory domain of the specified type. + * + * Translation functions can be provided to translate addresses from one memory domain to another. + * If the two domains both use the same addressing scheme for, then this translation does nothing. + * However, it is possible that the two memory domains may address the same physical memory + * differently, so this translation step is required. + * + * \param domain Double pointer to memory domain to be allocated by this function + * \param type Type of the DMA device which can access this memory domain + * \param ctx Optional memory domain context + * \param id String identifier representing the DMA device that can access this memory domain. + * \return 0 on success, negated errno on failure + */ +int spdk_memory_domain_create(struct spdk_memory_domain **domain, enum spdk_dma_device_type type, + struct spdk_memory_domain_ctx *ctx, const char *id); + +/** + * Set translation function for memory domain. Overwrites existing translation function. + * + * \param domain Memory domain + * \param translate_cb Translation function + */ +void spdk_memory_domain_set_translation(struct spdk_memory_domain *domain, + spdk_memory_domain_translate_memory_cb translate_cb); + +/** + * Set fetch function for memory domain. Overwrites existing fetch function. + * + * \param domain Memory domain + * \param fetch_cb Fetch function + */ +void spdk_memory_domain_set_fetch(struct spdk_memory_domain *domain, + spdk_memory_domain_fetch_data_cb fetch_cb); + +/** + * Get the context passed by the user in \ref spdk_memory_domain_create + * + * \param domain Memory domain + * \return Memory domain context + */ +struct spdk_memory_domain_ctx *spdk_memory_domain_get_context(struct spdk_memory_domain *domain); + +/** + * Get type of the DMA device that can access this memory domain + * + * \param domain Memory domain + * \return DMA device type + */ +enum spdk_dma_device_type spdk_memory_domain_get_dma_device_type(struct spdk_memory_domain *domain); + +/** + * Get an identifier representing the DMA device that can access this memory domain + * \param domain Memory domain + * \return DMA device identifier + */ +const char *spdk_memory_domain_get_dma_device_id(struct spdk_memory_domain *domain); + +/** + * Destroy memory domain + * + * \param domain Memory domain + */ +void spdk_memory_domain_destroy(struct spdk_memory_domain *domain); + +/** + * Asynchronously fetch data which is described by \b src_domain and located in \b src_iov to a location + * \b dst_iov local memory space. + * + * \param src_domain Memory domain in which space data buffer is located + * \param src_domain_ctx User defined context + * \param src_iov Source data iov + * \param src_iov_cnt The number of elements in \b src_iov + * \param dst_iov Destination iov + * \param dst_iov_cnt The number of elements in \b dst_iov + * \param cpl_cb Completion callback + * \param cpl_cb_arg Completion callback argument + * \return 0 on success, negated errno on failure. fetch_cb implementation must only call the callback when 0 + * is returned + */ +int spdk_memory_domain_fetch_data(struct spdk_memory_domain *src_domain, void *src_domain_ctx, + struct iovec *src_iov, uint32_t src_iov_cnt, struct iovec *dst_iov, uint32_t dst_iov_cnt, + spdk_memory_domain_fetch_data_cpl_cb cpl_cb, void *cpl_cb_arg); + +/** + * Translate data located in \b src_domain space at address \b addr with size \b len into an equivalent + * description of memory in dst_domain. + * + * This function calls \b src_domain translation callback, the callback needs to be set using \ref + * spdk_memory_domain_set_translation function. + * No data is moved during this operation. Both src_domain and dst_domain must describe the same physical memory, + * just from the point of view of two different memory domain. This is a translation of the description of the memory only. + * Result of translation is stored in \b result, its content depends on the type of \b dst_domain. + * + * \param src_domain Memory domain in which address space data buffer is located + * \param src_domain_ctx User defined context + * \param dst_domain Memory domain in which memory space data buffer should be translated + * \param dst_domain_ctx Ancillary data for dst_domain + * \param addr Addres in \b src_domain memory space + * \param len Length of the data + * \param result Translation result. The content of the translation result is only valid if this + * function returns 0. + * \return 0 on success, negated errno on failure. + */ +int spdk_memory_domain_translate_data(struct spdk_memory_domain *src_domain, void *src_domain_ctx, + struct spdk_memory_domain *dst_domain, struct spdk_memory_domain_translation_ctx *dst_domain_ctx, + void *addr, size_t len, struct spdk_memory_domain_translation_result *result); + +/** + * Get the first memory domain. + * + * Combined with \ref spdk_memory_domain_get_next to iterate over all memory domains + * + * \param id Optional identifier representing the DMA device that can access a memory domain, if set + * then this function returns the first memory domain which id matches or NULL + * \return Pointer to the first memory domain or NULL + */ +struct spdk_memory_domain *spdk_memory_domain_get_first(const char *id); + +/** + * Get the next memory domain. + * + * \param prev Previous memory domain + * \param id Optional identifier representing the DMA device that can access a memory domain, if set + * then this function returns the next memory domain which id matches or NULL + * \return Pointer to next memory domain or NULL; + */ +struct spdk_memory_domain *spdk_memory_domain_get_next(struct spdk_memory_domain *prev, + const char *id); + + +#ifdef __cplusplus +} +#endif + +#endif /* SPDK_DMA_H */ diff --git a/lib/Makefile b/lib/Makefile index cd1ddb9cf..78a27bba2 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -35,7 +35,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk include $(SPDK_ROOT_DIR)/mk/spdk.lib_deps.mk -DIRS-y += bdev blob blobfs conf accel event json jsonrpc \ +DIRS-y += bdev blob blobfs conf dma accel event json jsonrpc \ log lvol rpc sock thread trace util nvme vmd nvmf scsi \ ioat ut_mock iscsi notify init ifeq ($(OS),Linux) diff --git a/lib/dma/Makefile b/lib/dma/Makefile new file mode 100644 index 000000000..24e5710d6 --- /dev/null +++ b/lib/dma/Makefile @@ -0,0 +1,45 @@ +# +# BSD LICENSE +# +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Nvidia Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +SO_VER := 1 +SO_MINOR := 0 +SO_SUFFIX := $(SO_VER).$(SO_MINOR) + +LIBNAME = dma +C_SRCS = dma.c + +SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_dma.map) + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/lib/dma/dma.c b/lib/dma/dma.c new file mode 100644 index 000000000..8d21b8ceb --- /dev/null +++ b/lib/dma/dma.c @@ -0,0 +1,252 @@ +/*- + * BSD LICENSE + * + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Nvidia Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/dma.h" +#include "spdk/log.h" +#include "spdk/util.h" +#include "spdk/likely.h" + +pthread_mutex_t g_dma_mutex = PTHREAD_MUTEX_INITIALIZER; +TAILQ_HEAD(, spdk_memory_domain) g_dma_memory_domains = TAILQ_HEAD_INITIALIZER( + g_dma_memory_domains); + +struct spdk_memory_domain { + enum spdk_dma_device_type type; + spdk_memory_domain_fetch_data_cb fetch_cb; + spdk_memory_domain_translate_memory_cb translate_cb; + TAILQ_ENTRY(spdk_memory_domain) link; + struct spdk_memory_domain_ctx *ctx; + char *id; +}; + +int +spdk_memory_domain_create(struct spdk_memory_domain **_domain, enum spdk_dma_device_type type, + struct spdk_memory_domain_ctx *ctx, const char *id) +{ + struct spdk_memory_domain *domain; + size_t ctx_size; + + if (!_domain) { + return -EINVAL; + } + + if (ctx && ctx->size == 0) { + SPDK_ERRLOG("Context size can't be 0\n"); + return -EINVAL; + } + + domain = calloc(1, sizeof(*domain)); + if (!domain) { + SPDK_ERRLOG("Failed to allocate memory"); + return -ENOMEM; + } + + if (id) { + domain->id = strdup(id); + if (!domain->id) { + SPDK_ERRLOG("Failed to allocate memory"); + free(domain); + return -ENOMEM; + } + } + + if (ctx) { + domain->ctx = calloc(1, sizeof(*domain->ctx)); + if (!domain->ctx) { + SPDK_ERRLOG("Failed to allocate memory"); + free(domain->id); + free(domain); + return -ENOMEM; + } + + ctx_size = spdk_min(sizeof(*domain->ctx), ctx->size); + memcpy(domain->ctx, ctx, ctx_size); + domain->ctx->size = ctx_size; + } + + domain->type = type; + + pthread_mutex_lock(&g_dma_mutex); + TAILQ_INSERT_TAIL(&g_dma_memory_domains, domain, link); + pthread_mutex_unlock(&g_dma_mutex); + + *_domain = domain; + + return 0; +} + +void +spdk_memory_domain_set_translation(struct spdk_memory_domain *domain, + spdk_memory_domain_translate_memory_cb translate_cb) +{ + if (!domain) { + return; + } + + domain->translate_cb = translate_cb; +} + +void +spdk_memory_domain_set_fetch(struct spdk_memory_domain *domain, + spdk_memory_domain_fetch_data_cb fetch_cb) +{ + if (!domain) { + return; + } + + domain->fetch_cb = fetch_cb; +} + +struct spdk_memory_domain_ctx * +spdk_memory_domain_get_context(struct spdk_memory_domain *domain) +{ + assert(domain); + + return domain->ctx; +} + +enum spdk_dma_device_type spdk_memory_domain_get_dma_device_type(struct spdk_memory_domain *domain) +{ + assert(domain); + + return domain->type; +} + +const char * +spdk_memory_domain_get_dma_device_id(struct spdk_memory_domain *domain) +{ + assert(domain); + + return domain->id; +} + +void +spdk_memory_domain_destroy(struct spdk_memory_domain *domain) +{ + if (!domain) { + return; + } + + pthread_mutex_lock(&g_dma_mutex); + TAILQ_REMOVE(&g_dma_memory_domains, domain, link); + pthread_mutex_unlock(&g_dma_mutex); + + free(domain->ctx); + free(domain->id); + free(domain); +} + +int +spdk_memory_domain_fetch_data(struct spdk_memory_domain *src_domain, void *src_domain_ctx, + struct iovec *src_iov, uint32_t src_iov_cnt, struct iovec *dst_iov, uint32_t dst_iov_cnt, + spdk_memory_domain_fetch_data_cpl_cb cpl_cb, void *cpl_cb_arg) +{ + assert(src_domain); + assert(src_iov); + assert(dst_iov); + + if (spdk_unlikely(!src_domain->fetch_cb)) { + return -ENOTSUP; + } + + return src_domain->fetch_cb(src_domain, src_domain_ctx, src_iov, src_iov_cnt, dst_iov, dst_iov_cnt, + cpl_cb, cpl_cb_arg); +} + +int +spdk_memory_domain_translate_data(struct spdk_memory_domain *src_domain, void *src_domain_ctx, + struct spdk_memory_domain *dst_domain, struct spdk_memory_domain_translation_ctx *dst_domain_ctx, + void *addr, size_t len, struct spdk_memory_domain_translation_result *result) +{ + assert(src_domain); + assert(dst_domain); + assert(result); + + if (spdk_unlikely(!src_domain->translate_cb)) { + return -ENOTSUP; + } + + return src_domain->translate_cb(src_domain, src_domain_ctx, dst_domain, dst_domain_ctx, addr, len, + result); +} + +struct spdk_memory_domain * +spdk_memory_domain_get_first(const char *id) +{ + struct spdk_memory_domain *domain; + + if (!id) { + pthread_mutex_lock(&g_dma_mutex); + domain = TAILQ_FIRST(&g_dma_memory_domains); + pthread_mutex_unlock(&g_dma_mutex); + + return domain; + } + + pthread_mutex_lock(&g_dma_mutex); + TAILQ_FOREACH(domain, &g_dma_memory_domains, link) { + if (!strcmp(domain->id, id)) { + break; + } + } + pthread_mutex_unlock(&g_dma_mutex); + + return domain; +} + +struct spdk_memory_domain * +spdk_memory_domain_get_next(struct spdk_memory_domain *prev, const char *id) +{ + struct spdk_memory_domain *domain; + + if (!prev) { + return NULL; + } + + pthread_mutex_lock(&g_dma_mutex); + domain = TAILQ_NEXT(prev, link); + pthread_mutex_unlock(&g_dma_mutex); + + if (!id || !domain) { + return domain; + } + + pthread_mutex_lock(&g_dma_mutex); + TAILQ_FOREACH_FROM(domain, &g_dma_memory_domains, link) { + if (!strcmp(domain->id, id)) { + break; + } + } + pthread_mutex_unlock(&g_dma_mutex); + + return domain; +} diff --git a/lib/dma/spdk_dma.map b/lib/dma/spdk_dma.map new file mode 100644 index 000000000..2edc9fced --- /dev/null +++ b/lib/dma/spdk_dma.map @@ -0,0 +1,18 @@ +{ + global: + + # public functions + spdk_memory_domain_create; + spdk_memory_domain_set_translation; + spdk_memory_domain_set_fetch; + spdk_memory_domain_get_context; + spdk_memory_domain_get_dma_device_type; + spdk_memory_domain_get_dma_device_id; + spdk_memory_domain_destroy; + spdk_memory_domain_fetch_data; + spdk_memory_domain_translate_data; + spdk_memory_domain_get_first; + spdk_memory_domain_get_next; + + local: *; +}; diff --git a/mk/spdk.lib_deps.mk b/mk/spdk.lib_deps.mk index ffc1611fe..6bab77535 100644 --- a/mk/spdk.lib_deps.mk +++ b/mk/spdk.lib_deps.mk @@ -51,6 +51,7 @@ DEPDIRS-idxd := log DEPDIRS-sock := log $(JSON_LIBS) DEPDIRS-util := log DEPDIRS-vmd := log +DEPDIRS-dma := log ifeq ($(CONFIG_VFIO_USER),y) DEPDIRS-vfio_user := log endif diff --git a/test/unit/lib/Makefile b/test/unit/lib/Makefile index a7ed1eea5..335ecd2db 100644 --- a/test/unit/lib/Makefile +++ b/test/unit/lib/Makefile @@ -34,7 +34,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk -DIRS-y = accel bdev blob blobfs event ioat iscsi json jsonrpc log lvol +DIRS-y = accel bdev blob blobfs dma event ioat iscsi json jsonrpc log lvol DIRS-y += notify nvme nvmf scsi sock thread util env_dpdk init DIRS-$(CONFIG_IDXD) += idxd DIRS-$(CONFIG_REDUCE) += reduce diff --git a/test/unit/lib/dma/Makefile b/test/unit/lib/dma/Makefile new file mode 100644 index 000000000..21002c3d6 --- /dev/null +++ b/test/unit/lib/dma/Makefile @@ -0,0 +1,43 @@ +# +# BSD LICENSE +# +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Nvidia Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +DIRS-y = dma.c + +.PHONY: all clean $(DIRS-y) + +all: $(DIRS-y) +clean: $(DIRS-y) + +include $(SPDK_ROOT_DIR)/mk/spdk.subdirs.mk diff --git a/test/unit/lib/dma/dma.c/.gitignore b/test/unit/lib/dma/dma.c/.gitignore new file mode 100644 index 000000000..8c849e1d4 --- /dev/null +++ b/test/unit/lib/dma/dma.c/.gitignore @@ -0,0 +1 @@ +dma_ut diff --git a/test/unit/lib/dma/dma.c/Makefile b/test/unit/lib/dma/dma.c/Makefile new file mode 100644 index 000000000..e23f5b4a1 --- /dev/null +++ b/test/unit/lib/dma/dma.c/Makefile @@ -0,0 +1,37 @@ +# +# BSD LICENSE +# +# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Nvidia Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk +TEST_FILE = dma_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/test/unit/lib/dma/dma.c/dma_ut.c b/test/unit/lib/dma/dma.c/dma_ut.c new file mode 100644 index 000000000..7f19beb67 --- /dev/null +++ b/test/unit/lib/dma/dma.c/dma_ut.c @@ -0,0 +1,214 @@ +/*- + * BSD LICENSE + * + * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Nvidia Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/stdinc.h" +#include "spdk_cunit.h" +#include "common/lib/test_env.c" +#include "unit/lib/json_mock.c" +#include "dma/dma.c" + +static bool g_memory_domain_fetch_called; +static bool g_memory_domain_translate_called; +static int g_memory_domain_cb_rc = 123; + +static void +test_memory_domain_fetch_data_cpl_cb(void *ctx, void *iov, uint32_t iovcnt, int rc) +{ +} + +static int test_memory_domain_fetch_data_cb(struct spdk_memory_domain *src_device, + void *src_device_ctx, struct iovec *src_iov, uint32_t src_iovcnt, struct iovec *dst_iov, + uint32_t dst_iovcnt, spdk_memory_domain_fetch_data_cpl_cb cpl_cb, void *cpl_cb_arg) +{ + g_memory_domain_fetch_called = true; + + return g_memory_domain_cb_rc; +} + +static int +test_memory_domain_translate_memory_cb(struct spdk_memory_domain *src_device, void *src_device_ctx, + struct spdk_memory_domain *dst_device, struct spdk_memory_domain_translation_ctx *dst_device_ctx, + void *addr, size_t len, struct spdk_memory_domain_translation_result *result) +{ + g_memory_domain_translate_called = true; + + return g_memory_domain_cb_rc; +} + +static void +test_dma(void) +{ + void *test_ibv_pd = (void *)0xdeadbeaf; + struct iovec src_iov = {}, dst_iov = {}; + struct spdk_memory_domain *domain = NULL, *domain_2 = NULL, *domain_3 = NULL; + struct spdk_memory_domain_ctx memory_domain_ctx = { + .rdma = { .ibv_pd = test_ibv_pd } + }; + struct spdk_memory_domain_ctx *stored_memory_domain_ctx; + struct spdk_memory_domain_translation_result translation_result; + const char *id; + int rc; + + /* Create memory domain. No device ptr, expect fail */ + rc = spdk_memory_domain_create(NULL, SPDK_DMA_DEVICE_TYPE_RDMA, &memory_domain_ctx, "test"); + CU_ASSERT(rc != 0); + + /* Create memory domain. ctx with zero size, expect fail */ + memory_domain_ctx.size = 0; + rc = spdk_memory_domain_create(&domain, SPDK_DMA_DEVICE_TYPE_RDMA, &memory_domain_ctx, "test"); + CU_ASSERT(rc != 0); + + /* Create memory domain. expect pass */ + memory_domain_ctx.size = sizeof(memory_domain_ctx); + rc = spdk_memory_domain_create(&domain, SPDK_DMA_DEVICE_TYPE_RDMA, &memory_domain_ctx, "test"); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(domain != NULL); + + /* Get context. Expect pass */ + stored_memory_domain_ctx = spdk_memory_domain_get_context(domain); + SPDK_CU_ASSERT_FATAL(stored_memory_domain_ctx != NULL); + CU_ASSERT(stored_memory_domain_ctx->rdma.ibv_pd == test_ibv_pd); + + /* Get DMA device type. Expect pass */ + CU_ASSERT(spdk_memory_domain_get_dma_device_type(domain) == SPDK_DMA_DEVICE_TYPE_RDMA); + + /* Get DMA id. Expect pass */ + id = spdk_memory_domain_get_dma_device_id(domain); + CU_ASSERT((!strcmp(id, domain->id))); + + /* Fetch data, callback is NULL. Expect fail */ + g_memory_domain_fetch_called = false; + rc = spdk_memory_domain_fetch_data(domain, NULL, &src_iov, 1, &dst_iov, 1, + test_memory_domain_fetch_data_cpl_cb, NULL); + CU_ASSERT(rc == -ENOTSUP); + CU_ASSERT(g_memory_domain_fetch_called == false); + + /* Set fetch callback */ + spdk_memory_domain_set_fetch(domain, test_memory_domain_fetch_data_cb); + + /* Fetch data. Expect pass */ + rc = spdk_memory_domain_fetch_data(domain, NULL, &src_iov, 1, &dst_iov, 1, + test_memory_domain_fetch_data_cpl_cb, NULL); + CU_ASSERT(rc == g_memory_domain_cb_rc); + CU_ASSERT(g_memory_domain_fetch_called == true); + + /* Translate data, callback is NULL. Expect fail */ + g_memory_domain_translate_called = false; + rc = spdk_memory_domain_translate_data(domain, NULL, domain, NULL, (void *)0xfeeddbeef, 0x1000, + &translation_result); + CU_ASSERT(rc == -ENOTSUP); + CU_ASSERT(g_memory_domain_translate_called == false); + + /* Set translate callback */ + spdk_memory_domain_set_translation(domain, test_memory_domain_translate_memory_cb); + + /* Translate data. Expect pass */ + g_memory_domain_translate_called = false; + rc = spdk_memory_domain_translate_data(domain, NULL, domain, NULL, (void *)0xfeeddbeef, 0x1000, + &translation_result); + CU_ASSERT(rc == g_memory_domain_cb_rc); + CU_ASSERT(g_memory_domain_translate_called == true); + + /* Set translation callback to NULL. Expect pass */ + spdk_memory_domain_set_translation(domain, NULL); + CU_ASSERT(domain->translate_cb == NULL); + + /* Set translation callback. Expect pass */ + spdk_memory_domain_set_translation(domain, test_memory_domain_translate_memory_cb); + CU_ASSERT(domain->translate_cb == test_memory_domain_translate_memory_cb); + + /* Set fetch callback to NULL. Expect pass */ + spdk_memory_domain_set_fetch(domain, NULL); + CU_ASSERT(domain->fetch_cb == NULL); + + /* Set translation_callback. Expect pass */ + spdk_memory_domain_set_fetch(domain, test_memory_domain_fetch_data_cb); + CU_ASSERT(domain->fetch_cb == test_memory_domain_fetch_data_cb); + + /* Create 2nd and 3rd memory domains with equal id to test enumeration */ + rc = spdk_memory_domain_create(&domain_2, SPDK_DMA_DEVICE_TYPE_RDMA, &memory_domain_ctx, "test_2"); + CU_ASSERT(rc == 0); + + rc = spdk_memory_domain_create(&domain_3, SPDK_DMA_DEVICE_TYPE_RDMA, &memory_domain_ctx, "test_2"); + CU_ASSERT(rc == 0); + + CU_ASSERT(spdk_memory_domain_get_first("test") == domain); + CU_ASSERT(spdk_memory_domain_get_next(domain, "test") == NULL); + CU_ASSERT(spdk_memory_domain_get_first("test_2") == domain_2); + CU_ASSERT(spdk_memory_domain_get_next(domain_2, "test_2") == domain_3); + CU_ASSERT(spdk_memory_domain_get_next(domain_3, "test_2") == NULL); + + CU_ASSERT(spdk_memory_domain_get_first(NULL) == domain); + CU_ASSERT(spdk_memory_domain_get_next(domain, NULL) == domain_2); + CU_ASSERT(spdk_memory_domain_get_next(domain_2, NULL) == domain_3); + CU_ASSERT(spdk_memory_domain_get_next(domain_3, NULL) == NULL); + + /* Remove 2nd device, repeat iteration */ + spdk_memory_domain_destroy(domain_2); + CU_ASSERT(spdk_memory_domain_get_first(NULL) == domain); + CU_ASSERT(spdk_memory_domain_get_next(domain, NULL) == domain_3); + CU_ASSERT(spdk_memory_domain_get_next(domain_3, NULL) == NULL); + + /* Remove 3rd device, repeat iteration */ + spdk_memory_domain_destroy(domain_3); + CU_ASSERT(spdk_memory_domain_get_first(NULL) == domain); + CU_ASSERT(spdk_memory_domain_get_next(domain, NULL) == NULL); + CU_ASSERT(spdk_memory_domain_get_first("test_2") == NULL); + + /* Destroy memory domain, domain == NULL */ + spdk_memory_domain_destroy(NULL); + CU_ASSERT(spdk_memory_domain_get_first(NULL) == domain); + + /* Destroy memory domain */ + spdk_memory_domain_destroy(domain); + CU_ASSERT(spdk_memory_domain_get_first(NULL) == NULL); +} + +int +main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + CU_set_error_action(CUEA_ABORT); + CU_initialize_registry(); + + suite = CU_add_suite("dma_suite", NULL, NULL); + CU_ADD_TEST(suite, test_dma); + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + + return num_failures; +} diff --git a/test/unit/unittest.sh b/test/unit/unittest.sh index 2bf365747..36786d144 100755 --- a/test/unit/unittest.sh +++ b/test/unit/unittest.sh @@ -239,6 +239,7 @@ run_test "unittest_util" unittest_util if grep -q '#define SPDK_CONFIG_VHOST 1' $rootdir/include/spdk/config.h; then run_test "unittest_vhost" $valgrind $testdir/lib/vhost/vhost.c/vhost_ut fi +run_test "unittest_dma" $valgrind $testdir/lib/dma/dma.c/dma_ut run_test "unittest_init" unittest_init