[v14,1/6] memarea: introduce memarea library
Checks
Commit Message
The memarea library is an allocator of variable-size object which based
on a memory region.
This patch provides rte_memarea_create() and rte_memarea_destroy() API.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
MAINTAINERS | 5 +
doc/api/doxy-api-index.md | 3 +-
doc/api/doxy-api.conf.in | 1 +
doc/guides/prog_guide/index.rst | 1 +
doc/guides/prog_guide/memarea_lib.rst | 46 +++++++
doc/guides/rel_notes/release_23_03.rst | 6 +
lib/memarea/memarea_private.h | 58 +++++++++
lib/memarea/meson.build | 10 ++
lib/memarea/rte_memarea.c | 170 +++++++++++++++++++++++++
lib/memarea/rte_memarea.h | 122 ++++++++++++++++++
lib/memarea/version.map | 12 ++
lib/meson.build | 1 +
12 files changed, 434 insertions(+), 1 deletion(-)
create mode 100644 doc/guides/prog_guide/memarea_lib.rst
create mode 100644 lib/memarea/memarea_private.h
create mode 100644 lib/memarea/meson.build
create mode 100644 lib/memarea/rte_memarea.c
create mode 100644 lib/memarea/rte_memarea.h
create mode 100644 lib/memarea/version.map
Comments
On 2/9/2023 6:36 AM, Chengwen Feng wrote:
> The memarea library is an allocator of variable-size object which based
> on a memory region.
>
> This patch provides rte_memarea_create() and rte_memarea_destroy() API.
>
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
> Acked-by: Morten Brørup <mb@smartsharesystems.com>
> ---
> +#include "memarea_private.h"
> +
> +RTE_LOG_REGISTER_DEFAULT(rte_memarea_logtype, INFO);
> +#define RTE_MEMAREA_LOG(level, fmt, args...) \
> + rte_log(RTE_LOG_ ## level, rte_memarea_logtype, \
> + "MEMAREA: %s(): " fmt "\n", __func__, ## args)
> +
> +static int
> +memarea_check_param(const struct rte_memarea_param *init)
> +{
> +#define MEMAREA_MIN_SIZE 1024
I don't see this limitation being documented anywhere? This probably
should either be moved to `rte_memarea.h`, or at least called out in the
API documentation.
> + size_t len;
> +
> + if (init == NULL) {
> + RTE_MEMAREA_LOG(ERR, "init param is NULL!");
> + return -EINVAL;
> + }
> +
> + len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
> + if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
> + RTE_MEMAREA_LOG(ERR, "name size: %zu invalid!", len);
> + return -EINVAL;
> + }
> +
> + if (init->source != RTE_MEMAREA_SOURCE_HEAP &&
> + init->source != RTE_MEMAREA_SOURCE_LIBC &&
> + init->source != RTE_MEMAREA_SOURCE_MEMAREA) {
> + RTE_MEMAREA_LOG(ERR, "%s source: %d not supported!",
> + init->name, init->source);
> + return -EINVAL;
> + }
> +
> + if (init->total_sz < MEMAREA_MIN_SIZE) {
> + RTE_MEMAREA_LOG(ERR, "%s total-size: %zu too small!",
> + init->name, init->total_sz);
> + return -EINVAL;
> + }
> +
> + if (init->source == RTE_MEMAREA_SOURCE_MEMAREA && init->src_ma == NULL) {
> + RTE_MEMAREA_LOG(ERR, "%s source memarea is NULL!", init->name);
> + return -EINVAL;
> + }
> +
> + if (init->alg != RTE_MEMAREA_ALGORITHM_NEXTFIT) {
> + RTE_MEMAREA_LOG(ERR, "%s algorithm: %d not supported!",
> + init->name, init->alg);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static void *
> +memarea_alloc_from_libc(size_t size)
> +{
> +#ifdef RTE_EXEC_ENV_WINDOWS
> + return _aligned_malloc(size, RTE_CACHE_LINE_SIZE);
> +#else
> + void *ptr = NULL;
> + int ret;
> + ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
> + if (ret)
> + return NULL;
Would `ptr` not be NULL if ret wasn't 0?
> +struct rte_memarea *
> +rte_memarea_create(const struct rte_memarea_param *init)
> +{
> + struct memarea_objhdr *hdr, *guard_hdr;
> +#ifdef RTE_LIBRTE_MEMAREA_DEBUG
> + struct memarea_objtlr *tlr;
> +#endif
> + struct rte_memarea *ma;
> + size_t align_sz;
> + void *ptr;
> + int ret;
> +
> + ret = memarea_check_param(init);
> + if (ret)
> + return NULL;
> +
> + ptr = memarea_alloc_area(init);
> + if (ptr == NULL) {
> + RTE_MEMAREA_LOG(ERR, "%s alloc memory area fail!", init->name);
> + return NULL;
> + }
> +
> + ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
> + if (ma == NULL) {
> + memarea_free_area(init, ptr);
> + RTE_MEMAREA_LOG(ERR, "%s alloc management object fail!", init->name);
> + return NULL;
> + }
> +
> + hdr = ptr;
> + align_sz = RTE_ALIGN_FLOOR(init->total_sz, MEMAREA_OBJECT_SIZE_ALIGN);
> + guard_hdr = RTE_PTR_ADD(ptr, align_sz - sizeof(struct memarea_objhdr));
> +
> + ma->init = *init;
> + rte_spinlock_init(&ma->lock);
> + ma->area_base = ptr;
> + ma->guard_hdr = guard_hdr;
> + TAILQ_INIT(&ma->obj_list);
> + TAILQ_INIT(&ma->avail_list);
> +
> + TAILQ_INSERT_TAIL(&ma->obj_list, hdr, obj_next);
> + TAILQ_INSERT_TAIL(&ma->avail_list, hdr, avail_next);
> +#ifdef RTE_LIBRTE_MEMAREA_DEBUG
> + hdr->cookie = MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE;
> + tlr = RTE_PTR_SUB(guard_hdr, sizeof(struct memarea_objtlr));
> + tlr->cookie = MEMAREA_OBJECT_TRAILER_COOKIE;
> +#endif
> +
> + memset(guard_hdr, 0, sizeof(struct memarea_objhdr));
> + TAILQ_INSERT_AFTER(&ma->obj_list, hdr, guard_hdr, obj_next);
> + MEMAREA_OBJECT_MARK_ALLOCATED(guard_hdr);
> +#ifdef RTE_LIBRTE_MEMAREA_DEBUG
> + guard_hdr->cookie = MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE;
> + /* The guard object have no trailer cookie. */
> +#endif
Nitpicking, but can we move the #ifdef-ery into functions? E.g. have
something like
set_header_cookie(hdr);
...
set_trailer_cookie(guard_hdr);
and have them just be noops if RTE_LIBRTE_MEMAREA_DEBUG is not defined?
> +
> + return ma;
> +}
> +
> +void
> +rte_memarea_destroy(struct rte_memarea *ma)
> +{
> + if (ma == NULL)
> + return;
> + memarea_free_area(&ma->init, ma->area_base);
> + rte_free(ma);
> +}
> diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
> new file mode 100644
> index 0000000000..435dca293f
> --- /dev/null
> +++ b/lib/memarea/rte_memarea.h
> @@ -0,0 +1,122 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2023 HiSilicon Limited
> + */
> +
> +#ifndef RTE_MEMAREA_H
> +#define RTE_MEMAREA_H
> +
> +/**
> + * @file
> + * RTE Memarea.
> + *
> + * The memarea is an allocator of variable-size object which based on a memory
> + * region. It has the following features:
> + *
> + * - The memory region can be initialized from the following memory sources:
> + * 1. HEAP: e.g. invoke rte_malloc_socket.
> + * 2. LIBC: e.g. invoke posix_memalign.
> + * 3. Another memarea: it can be allocated from another memarea.
> + *
> + * - It supports MT-safe as long as it's specified at creation time. If not
> + * specified, all the functions of the memarea API are lock-free, and assume
> + * to not be invoked in parallel on different logical cores to work on the
> + * same memarea.
I think it would be nice to explicitly mention three things here:
1) that the alignment is always on cache line boundary
2) that the memory is not intended for DMA purposes
3) that secondary processes are not supported
Also, *technically*, "another memarea" is only supported starting at
commit 3, so I would suggest either adding stubs to support it in this
commit, or not add it to the enums.
> + */
> +
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +
> +#include <rte_compat.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +#define RTE_MEMAREA_NAMESIZE 64
> +
> +/**
> + * Memarea memory source.
> + */
> +enum rte_memarea_source {
> + /** Memory source comes from rte memory. */
DPDK memory? rte_malloc memory? I don't think we use the term "rte" to
refer to DPDK anywhere outside of legacy usages
> + RTE_MEMAREA_SOURCE_HEAP,
> + /** Memory source comes from libc. */
> + RTE_MEMAREA_SOURCE_LIBC,
> + /** Memory source comes from another memarea. */
> + RTE_MEMAREA_SOURCE_MEMAREA,
See above note about this not being supported in this commit.
> +};
> +
> +/**
> + * Memarea memory management algorithm.
> + */
> +enum rte_memarea_algorithm {
> + /** The default management algorithm is a variant of the next fit
> + * algorithm. It uses a free-list to apply for memory and uses an
> + * object-list in ascending order of address to support merging
> + * upon free.
> + */
> + RTE_MEMAREA_ALGORITHM_NEXTFIT,
> +};
> +
> +struct rte_memarea;
> +
> +/**
> + * Memarea creation parameters.
> + */
> +struct rte_memarea_param {
> + char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
> + enum rte_memarea_source source; /**< Memory source of memarea. */
> + enum rte_memarea_algorithm alg; /**< Memory management algorithm. */
> + size_t total_sz; /**< Total size (bytes) of memarea. */
> + /** Indicates whether the memarea API should be MT-safe. */
> + uint32_t mt_safe : 1;
> + union {
> + /** Numa socket from which to apply for memarea's memory, this
> + * field is valid only when the source is set to be
> + * RTE_MEMAREA_SOURCE_HEAP.
> + */
> + int numa_socket;
> + /** Source memarea, this field is valid only when the source is
> + * set to be RTE_MEMAREA_SOURCE_MEMAREA.
> + */
> + struct rte_memarea *src_ma;
Wouldn't it be better to have these fields inside structs indicating
relevant modes? E.g. param->heap.numa_socket as opposed to
param->numa_socket - I think this will be more self-explanatory in code.
Also, I think the convention around DPDK is to refer to NUMA sockets as
`socket_id` rather than `numa_socket` so for consistency I think it
would be nice if this was the case here as well.
On 2/9/2023 6:36 AM, Chengwen Feng wrote:
> The memarea library is an allocator of variable-size object which based
> on a memory region.
>
> This patch provides rte_memarea_create() and rte_memarea_destroy() API.
>
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
> Acked-by: Morten Brørup <mb@smartsharesystems.com>
> ---
> +static int
> +memarea_check_param(const struct rte_memarea_param *init)
> +{
> +#define MEMAREA_MIN_SIZE 1024
> + size_t len;
> +
> + if (init == NULL) {
> + RTE_MEMAREA_LOG(ERR, "init param is NULL!");
> + return -EINVAL;
> + }
> +
> + len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
> + if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
> + RTE_MEMAREA_LOG(ERR, "name size: %zu invalid!", len);
> + return -EINVAL;
> + }
> +
> + if (init->source != RTE_MEMAREA_SOURCE_HEAP &&
> + init->source != RTE_MEMAREA_SOURCE_LIBC &&
> + init->source != RTE_MEMAREA_SOURCE_MEMAREA) {
> + RTE_MEMAREA_LOG(ERR, "%s source: %d not supported!",
> + init->name, init->source);
> + return -EINVAL;
> + }
> +
> + if (init->total_sz < MEMAREA_MIN_SIZE) {
> + RTE_MEMAREA_LOG(ERR, "%s total-size: %zu too small!",
> + init->name, init->total_sz);
> + return -EINVAL;
> + }
> +
> + if (init->source == RTE_MEMAREA_SOURCE_MEMAREA && init->src_ma == NULL) {
> + RTE_MEMAREA_LOG(ERR, "%s source memarea is NULL!", init->name);
> + return -EINVAL;
> + }
> +
> + if (init->alg != RTE_MEMAREA_ALGORITHM_NEXTFIT) {
> + RTE_MEMAREA_LOG(ERR, "%s algorithm: %d not supported!",
> + init->name, init->alg);
> + return -EINVAL;
> + }
Also, you're returning a lot of `errno` type values from this function,
but you do not appear to use these values anywhere. I think it'd be
better if you used the return value to set `rte_errno` to indicate what
kind of error there was. The entire API could benefit from doing a pass
on setting rte_errno in error cases, and documenting them.
@@ -1575,6 +1575,11 @@ F: app/test/test_lpm*
F: app/test/test_func_reentrancy.c
F: app/test/test_xmmt_ops.h
+Memarea - EXPERIMENTAL
+M: Chengwen Feng <fengchengwen@huawei.com>
+F: lib/memarea
+F: doc/guides/prog_guide/memarea_lib.rst
+
Membership - EXPERIMENTAL
M: Yipeng Wang <yipeng1.wang@intel.com>
M: Sameh Gobriel <sameh.gobriel@intel.com>
@@ -62,7 +62,8 @@ The public API headers are grouped by topics:
[memzone](@ref rte_memzone.h),
[mempool](@ref rte_mempool.h),
[malloc](@ref rte_malloc.h),
- [memcpy](@ref rte_memcpy.h)
+ [memcpy](@ref rte_memcpy.h),
+ [memarea](@ref rte_memarea.h)
- **timers**:
[cycles](@ref rte_cycles.h),
@@ -53,6 +53,7 @@ INPUT = @TOPDIR@/doc/api/doxy-api-index.md \
@TOPDIR@/lib/latencystats \
@TOPDIR@/lib/lpm \
@TOPDIR@/lib/mbuf \
+ @TOPDIR@/lib/memarea \
@TOPDIR@/lib/member \
@TOPDIR@/lib/mempool \
@TOPDIR@/lib/meter \
@@ -37,6 +37,7 @@ Programmer's Guide
hash_lib
toeplitz_hash_lib
efd_lib
+ memarea_lib
member_lib
lpm_lib
lpm6_lib
new file mode 100644
@@ -0,0 +1,46 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+ Copyright(c) 2023 HiSilicon Limited
+
+Memarea Library
+===============
+
+Introduction
+------------
+
+The memarea library provides an allocator of variable-size objects, it is
+oriented towards the application layer, providing 'region-based memory
+management' function [1].
+
+The main features are as follows:
+
+* The memory region can be initialized from the following memory sources:
+
+ - HEAP: e.g. invoke ``rte_malloc_socket``.
+
+ - LIBC: e.g. invoke posix_memalign.
+
+ - Another memarea: it can be allocated from another memarea.
+
+* It supports MT-safe as long as it's specified at creation time.
+
+Library API Overview
+--------------------
+
+The ``rte_memarea_create()`` function is used to create a memarea, the function
+returns the pointer to the created memarea or ``NULL`` if the creation failed.
+
+The ``rte_memarea_destroy()`` function is used to destroy a memarea.
+
+Debug Mode
+----------
+
+In debug mode, cookies are added at the beginning and end of objects, it will
+help debugging buffer overflows.
+
+Debug mode is disabled by default, but can be enabled by setting
+``RTE_LIBRTE_MEMAREA_DEBUG`` in ``config/rte_config.h``.
+
+Reference
+---------
+
+[1] https://en.wikipedia.org/wiki/Region-based_memory_management
@@ -55,6 +55,12 @@ New Features
Also, make sure to start the actual text at the margin.
=======================================================
+* **Added memarea library.**
+
+ The memarea library is an allocator of variable-size objects, it is oriented
+ towards the application layer, providing 'region-based memory management'
+ function.
+
* **Updated AMD axgbe driver.**
* Added multi-process support.
new file mode 100644
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#ifndef MEMAREA_PRIVATE_H
+#define MEMAREA_PRIVATE_H
+
+#include <rte_memarea.h>
+
+#define MEMAREA_OBJECT_SIZE_ALIGN 8
+
+#define MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE 0xbeef1234beef1234ULL
+#define MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE 0x12345678abcdef12ULL
+#define MEMAREA_OBJECT_TRAILER_COOKIE 0xabcd1234abcd5678ULL
+
+#define MEMAREA_OBJECT_IS_ALLOCATED(hdr) (TAILQ_NEXT((hdr), avail_next) == (void *)-1)
+#define MEMAREA_OBJECT_MARK_ALLOCATED(hdr) (TAILQ_NEXT((hdr), avail_next) = (void *)-1)
+
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+#define MEMAREA_OBJECT_GET_SIZE(hdr) \
+ ((uintptr_t)TAILQ_NEXT((hdr), obj_next) - (uintptr_t)(hdr) - \
+ sizeof(struct memarea_objhdr) - sizeof(struct memarea_objtlr))
+#else
+#define MEMAREA_OBJECT_GET_SIZE(hdr) \
+ ((uintptr_t)TAILQ_NEXT((hdr), obj_next) - (uintptr_t)(hdr) - \
+ sizeof(struct memarea_objhdr))
+#endif
+
+struct memarea_objhdr {
+ /** The obj_next list is an address ascending ordered linked list. */
+ TAILQ_ENTRY(memarea_objhdr) obj_next;
+ /** The avail_next list is an unordered linked list. If it's tqe_next
+ * is -1, means it has been allocated.
+ */
+ TAILQ_ENTRY(memarea_objhdr) avail_next;
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+ uint64_t cookie; /**< Debug cookie */
+#endif
+};
+
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+struct memarea_objtlr {
+ uint64_t cookie; /**< Debug cookie */
+};
+#endif
+
+TAILQ_HEAD(memarea_objhdr_list, memarea_objhdr);
+
+struct rte_memarea {
+ struct rte_memarea_param init;
+ rte_spinlock_t lock;
+ void *area_base;
+ struct memarea_objhdr *guard_hdr;
+ struct memarea_objhdr_list obj_list;
+ struct memarea_objhdr_list avail_list;
+} __rte_cache_aligned;
+
+#endif /* MEMAREA_PRIVATE_H */
new file mode 100644
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 HiSilicon Limited
+
+sources = files(
+ 'rte_memarea.c',
+)
+headers = files(
+ 'rte_memarea.h',
+)
+deps += []
new file mode 100644
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <rte_common.h>
+#include <rte_log.h>
+#include <rte_malloc.h>
+#include <rte_spinlock.h>
+
+#include "rte_memarea.h"
+#include "memarea_private.h"
+
+RTE_LOG_REGISTER_DEFAULT(rte_memarea_logtype, INFO);
+#define RTE_MEMAREA_LOG(level, fmt, args...) \
+ rte_log(RTE_LOG_ ## level, rte_memarea_logtype, \
+ "MEMAREA: %s(): " fmt "\n", __func__, ## args)
+
+static int
+memarea_check_param(const struct rte_memarea_param *init)
+{
+#define MEMAREA_MIN_SIZE 1024
+ size_t len;
+
+ if (init == NULL) {
+ RTE_MEMAREA_LOG(ERR, "init param is NULL!");
+ return -EINVAL;
+ }
+
+ len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
+ if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
+ RTE_MEMAREA_LOG(ERR, "name size: %zu invalid!", len);
+ return -EINVAL;
+ }
+
+ if (init->source != RTE_MEMAREA_SOURCE_HEAP &&
+ init->source != RTE_MEMAREA_SOURCE_LIBC &&
+ init->source != RTE_MEMAREA_SOURCE_MEMAREA) {
+ RTE_MEMAREA_LOG(ERR, "%s source: %d not supported!",
+ init->name, init->source);
+ return -EINVAL;
+ }
+
+ if (init->total_sz < MEMAREA_MIN_SIZE) {
+ RTE_MEMAREA_LOG(ERR, "%s total-size: %zu too small!",
+ init->name, init->total_sz);
+ return -EINVAL;
+ }
+
+ if (init->source == RTE_MEMAREA_SOURCE_MEMAREA && init->src_ma == NULL) {
+ RTE_MEMAREA_LOG(ERR, "%s source memarea is NULL!", init->name);
+ return -EINVAL;
+ }
+
+ if (init->alg != RTE_MEMAREA_ALGORITHM_NEXTFIT) {
+ RTE_MEMAREA_LOG(ERR, "%s algorithm: %d not supported!",
+ init->name, init->alg);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void *
+memarea_alloc_from_libc(size_t size)
+{
+#ifdef RTE_EXEC_ENV_WINDOWS
+ return _aligned_malloc(size, RTE_CACHE_LINE_SIZE);
+#else
+ void *ptr = NULL;
+ int ret;
+ ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+ if (ret)
+ return NULL;
+ return ptr;
+ #endif
+}
+
+static void *
+memarea_alloc_area(const struct rte_memarea_param *init)
+{
+ void *ptr = NULL;
+
+ if (init->source == RTE_MEMAREA_SOURCE_HEAP)
+ ptr = rte_malloc_socket(NULL, init->total_sz, RTE_CACHE_LINE_SIZE,
+ init->numa_socket);
+ else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
+ ptr = memarea_alloc_from_libc(init->total_sz);
+
+ return ptr;
+}
+
+static void
+memarea_free_area(const struct rte_memarea_param *init, void *ptr)
+{
+ if (init->source == RTE_MEMAREA_SOURCE_HEAP)
+ rte_free(ptr);
+ else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
+ free(ptr);
+}
+
+struct rte_memarea *
+rte_memarea_create(const struct rte_memarea_param *init)
+{
+ struct memarea_objhdr *hdr, *guard_hdr;
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+ struct memarea_objtlr *tlr;
+#endif
+ struct rte_memarea *ma;
+ size_t align_sz;
+ void *ptr;
+ int ret;
+
+ ret = memarea_check_param(init);
+ if (ret)
+ return NULL;
+
+ ptr = memarea_alloc_area(init);
+ if (ptr == NULL) {
+ RTE_MEMAREA_LOG(ERR, "%s alloc memory area fail!", init->name);
+ return NULL;
+ }
+
+ ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
+ if (ma == NULL) {
+ memarea_free_area(init, ptr);
+ RTE_MEMAREA_LOG(ERR, "%s alloc management object fail!", init->name);
+ return NULL;
+ }
+
+ hdr = ptr;
+ align_sz = RTE_ALIGN_FLOOR(init->total_sz, MEMAREA_OBJECT_SIZE_ALIGN);
+ guard_hdr = RTE_PTR_ADD(ptr, align_sz - sizeof(struct memarea_objhdr));
+
+ ma->init = *init;
+ rte_spinlock_init(&ma->lock);
+ ma->area_base = ptr;
+ ma->guard_hdr = guard_hdr;
+ TAILQ_INIT(&ma->obj_list);
+ TAILQ_INIT(&ma->avail_list);
+
+ TAILQ_INSERT_TAIL(&ma->obj_list, hdr, obj_next);
+ TAILQ_INSERT_TAIL(&ma->avail_list, hdr, avail_next);
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+ hdr->cookie = MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE;
+ tlr = RTE_PTR_SUB(guard_hdr, sizeof(struct memarea_objtlr));
+ tlr->cookie = MEMAREA_OBJECT_TRAILER_COOKIE;
+#endif
+
+ memset(guard_hdr, 0, sizeof(struct memarea_objhdr));
+ TAILQ_INSERT_AFTER(&ma->obj_list, hdr, guard_hdr, obj_next);
+ MEMAREA_OBJECT_MARK_ALLOCATED(guard_hdr);
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+ guard_hdr->cookie = MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE;
+ /* The guard object have no trailer cookie. */
+#endif
+
+ return ma;
+}
+
+void
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+ if (ma == NULL)
+ return;
+ memarea_free_area(&ma->init, ma->area_base);
+ rte_free(ma);
+}
new file mode 100644
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#ifndef RTE_MEMAREA_H
+#define RTE_MEMAREA_H
+
+/**
+ * @file
+ * RTE Memarea.
+ *
+ * The memarea is an allocator of variable-size object which based on a memory
+ * region. It has the following features:
+ *
+ * - The memory region can be initialized from the following memory sources:
+ * 1. HEAP: e.g. invoke rte_malloc_socket.
+ * 2. LIBC: e.g. invoke posix_memalign.
+ * 3. Another memarea: it can be allocated from another memarea.
+ *
+ * - It supports MT-safe as long as it's specified at creation time. If not
+ * specified, all the functions of the memarea API are lock-free, and assume
+ * to not be invoked in parallel on different logical cores to work on the
+ * same memarea.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <rte_compat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTE_MEMAREA_NAMESIZE 64
+
+/**
+ * Memarea memory source.
+ */
+enum rte_memarea_source {
+ /** Memory source comes from rte memory. */
+ RTE_MEMAREA_SOURCE_HEAP,
+ /** Memory source comes from libc. */
+ RTE_MEMAREA_SOURCE_LIBC,
+ /** Memory source comes from another memarea. */
+ RTE_MEMAREA_SOURCE_MEMAREA,
+};
+
+/**
+ * Memarea memory management algorithm.
+ */
+enum rte_memarea_algorithm {
+ /** The default management algorithm is a variant of the next fit
+ * algorithm. It uses a free-list to apply for memory and uses an
+ * object-list in ascending order of address to support merging
+ * upon free.
+ */
+ RTE_MEMAREA_ALGORITHM_NEXTFIT,
+};
+
+struct rte_memarea;
+
+/**
+ * Memarea creation parameters.
+ */
+struct rte_memarea_param {
+ char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
+ enum rte_memarea_source source; /**< Memory source of memarea. */
+ enum rte_memarea_algorithm alg; /**< Memory management algorithm. */
+ size_t total_sz; /**< Total size (bytes) of memarea. */
+ /** Indicates whether the memarea API should be MT-safe. */
+ uint32_t mt_safe : 1;
+ union {
+ /** Numa socket from which to apply for memarea's memory, this
+ * field is valid only when the source is set to be
+ * RTE_MEMAREA_SOURCE_HEAP.
+ */
+ int numa_socket;
+ /** Source memarea, this field is valid only when the source is
+ * set to be RTE_MEMAREA_SOURCE_MEMAREA.
+ */
+ struct rte_memarea *src_ma;
+ };
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Create memarea.
+ *
+ * Create one new memarea.
+ *
+ * @param init
+ * The init parameter of memarea.
+ *
+ * @return
+ * Non-NULL on success. Otherwise NULL is returned.
+ */
+__rte_experimental
+struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Destroy memarea.
+ *
+ * Destroy the memarea.
+ *
+ * @param ma
+ * The pointer of memarea.
+ */
+__rte_experimental
+void rte_memarea_destroy(struct rte_memarea *ma);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RTE_MEMAREA_H */
new file mode 100644
@@ -0,0 +1,12 @@
+EXPERIMENTAL {
+ global:
+
+ rte_memarea_create;
+ rte_memarea_destroy;
+
+ local: *;
+};
+
+INTERNAL {
+ local: *;
+};
@@ -42,6 +42,7 @@ libraries = [
'kni',
'latencystats',
'lpm',
+ 'memarea',
'member',
'pcapng',
'power',