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 8A4E142E2C; Sun, 9 Jul 2023 15:09:16 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id D2276410FA; Sun, 9 Jul 2023 15:08:55 +0200 (CEST) Received: from szxga08-in.huawei.com (szxga08-in.huawei.com [45.249.212.255]) by mails.dpdk.org (Postfix) with ESMTP id 0C1F9410FA for ; Sun, 9 Jul 2023 15:08:48 +0200 (CEST) Received: from dggpeml100024.china.huawei.com (unknown [172.30.72.53]) by szxga08-in.huawei.com (SkyGuard) with ESMTP id 4QzS9q450lz1FDWW; Sun, 9 Jul 2023 21:08:15 +0800 (CST) Received: from localhost.localdomain (10.50.163.32) by dggpeml100024.china.huawei.com (7.185.36.115) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.27; Sun, 9 Jul 2023 21:08:46 +0800 From: Chengwen Feng To: , CC: , , , , , , Subject: [PATCH v15 1/6] memarea: introduce memarea library Date: Sun, 9 Jul 2023 13:00:36 +0000 Message-ID: <20230709130041.8736-2-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230709130041.8736-1-fengchengwen@huawei.com> References: <20220721044648.6817-1-fengchengwen@huawei.com> <20230709130041.8736-1-fengchengwen@huawei.com> MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8bit X-Originating-IP: [10.50.163.32] X-ClientProxiedBy: dggems702-chm.china.huawei.com (10.3.19.179) To dggpeml100024.china.huawei.com (7.185.36.115) X-CFilter-Loop: Reflected 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 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 Reviewed-by: Dongdong Liu Acked-by: Morten Brørup --- 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 | 48 ++++++ doc/guides/rel_notes/release_23_07.rst | 6 + lib/memarea/memarea_private.h | 86 ++++++++++ lib/memarea/meson.build | 10 ++ lib/memarea/rte_memarea.c | 215 +++++++++++++++++++++++++ lib/memarea/rte_memarea.h | 137 ++++++++++++++++ lib/memarea/version.map | 12 ++ lib/meson.build | 1 + 12 files changed, 524 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 diff --git a/MAINTAINERS b/MAINTAINERS index 1c3be16a91..93de0876f1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1612,6 +1612,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 +F: lib/memarea +F: doc/guides/prog_guide/memarea_lib.rst + Membership - EXPERIMENTAL M: Yipeng Wang M: Sameh Gobriel diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md index 3bc8778981..5c32913f92 100644 --- a/doc/api/doxy-api-index.md +++ b/doc/api/doxy-api-index.md @@ -65,7 +65,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), diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in index 1a4210b948..1f35d8483e 100644 --- a/doc/api/doxy-api.conf.in +++ b/doc/api/doxy-api.conf.in @@ -54,6 +54,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 \ diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index d89cd3edb6..aa8eebe256 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -38,6 +38,7 @@ Programmer's Guide hash_lib toeplitz_hash_lib efd_lib + memarea_lib member_lib lpm_lib lpm6_lib diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst new file mode 100644 index 0000000000..bf19090294 --- /dev/null +++ b/doc/guides/prog_guide/memarea_lib.rst @@ -0,0 +1,48 @@ +.. 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. + +* The address returned by the allocator is align to 8B. + +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 diff --git a/doc/guides/rel_notes/release_23_07.rst b/doc/guides/rel_notes/release_23_07.rst index dcfd692425..ccef40e8cb 100644 --- a/doc/guides/rel_notes/release_23_07.rst +++ b/doc/guides/rel_notes/release_23_07.rst @@ -206,6 +206,12 @@ New Features See the :doc:`../tools/dmaperf` for more details. +* **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. + Removed Items ------------- diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h new file mode 100644 index 0000000000..61dc518777 --- /dev/null +++ b/lib/memarea/memarea_private.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#ifndef MEMAREA_PRIVATE_H +#define MEMAREA_PRIVATE_H + +#include + +#define MEMAREA_MINIMUM_TOTAL_SIZE 1024 + +#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 will form obj_lsit. */ + TAILQ_ENTRY(memarea_objhdr) obj_next; + /** If the object is available, the avail_next will link in avail_list. + * If the object has been allocated, the avail_next.tqe_next is -1. + */ + 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; + /** The obj_list is an address ascending ordered linked list: + * ---------------------- -------------- + * | object-1 | | object-1 | + * obj_list -> |~~~~~~~~~~~~~~~~~~~~| data-region |~~~~~~~~~~~~| + * ---> | tailq | hdr-cookie | | tlr-cookie | + * | ---------------------- -------------- + * | + * | ---------------------- -------------- + * | | object-2 | | object-2 | + * ---> |~~~~~~~~~~~~~~~~~~~~| data-region |~~~~~~~~~~~~| + * ---> | tailq | hdr-cookie | | tlr-cookie | + * | ---------------------- -------------- + * ... + * ... more objects. + * ... + * | ---------------------- + * | | object-guard | + * ---> |~~~~~~~~~~~~~~~~~~~~| + * | tailq | hdr-cookie | + * ---------------------- + * Note: the last object is the guard object, which has no data-region + * and no tailer cookie. + **/ + struct memarea_objhdr_list obj_list; + /** The avail_list is an unordered linked list. This list will hold the + * objects which are available(means can be used to allocate). + */ + struct memarea_objhdr_list avail_list; +} __rte_cache_aligned; + +#endif /* MEMAREA_PRIVATE_H */ diff --git a/lib/memarea/meson.build b/lib/memarea/meson.build new file mode 100644 index 0000000000..5d2632a38e --- /dev/null +++ b/lib/memarea/meson.build @@ -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 += [] diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c new file mode 100644 index 0000000000..70ac08a572 --- /dev/null +++ b/lib/memarea/rte_memarea.c @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#include +#include + +#include +#include +#include +#include +#include + +#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) +{ + 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_MINIMUM_TOTAL_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->ma.src == 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 != 0) + 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->heap.socket_id); + 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); +} + +/** + * Set cookie. + * + * @param status + * - 0: object is set to be available, but don't set tailer cookie. + * - 1: object is set to be allocated, but don't set tailer cookie. + * - 2: object is new split, the header cookie will set to be available, + * the tailer cookie of the previous object will be set. + * - 3: object is new split, the header cookie will set to be allocated, + * the tailer cookie of the previous object will be set. + * - 4: object is to be merged, it will no longer exist. the header cookie + * is cleared and the tailer cookie of the previous object is cleared. + */ +static inline void +memarea_set_cookie(struct memarea_objhdr *hdr, int status) +{ +#ifdef RTE_LIBRTE_MEMAREA_DEBUG + struct memarea_objtlr *tlr; + + if (status == 0) { + hdr->cookie = MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE; + } else if (status == 1) { + hdr->cookie = MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE; + } else if (status == 2) { + hdr->cookie = MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE; + tlr = RTE_PTR_SUB(hdr, sizeof(struct memarea_objtlr)); + tlr->cookie = MEMAREA_OBJECT_TRAILER_COOKIE; + } else if (status == 3) { + hdr->cookie = MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE; + tlr = RTE_PTR_SUB(hdr, sizeof(struct memarea_objtlr)); + tlr->cookie = MEMAREA_OBJECT_TRAILER_COOKIE; + } else if (status == 4) { + hdr->cookie = 0; + tlr = RTE_PTR_SUB(hdr, sizeof(struct memarea_objtlr)); + tlr->cookie = 0; + } +#else + RTE_SET_USED(hdr); + RTE_SET_USED(status); +#endif +} + +struct rte_memarea * +rte_memarea_create(const struct rte_memarea_param *init) +{ + struct memarea_objhdr *hdr, *guard_hdr; + struct rte_memarea *ma; + size_t align_sz; + void *ptr; + int ret; + + /** 1st: check parameter valid. */ + ret = memarea_check_param(init); + if (ret != 0) { + rte_errno = -ret; + return NULL; + } + + /** 2nd: alloc the memarea data region. */ + ptr = memarea_alloc_area(init); + if (ptr == NULL) { + RTE_MEMAREA_LOG(ERR, "%s alloc memory area fail!", init->name); + rte_errno = ENOMEM; + return NULL; + } + + /** 3rd: alloc the memare management struct. */ + 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); + rte_errno = ENOMEM; + return NULL; + } + + /** 4th: backup init parameter, initialize the lock and list. */ + ma->init = *init; + rte_spinlock_init(&ma->lock); + TAILQ_INIT(&ma->obj_list); + TAILQ_INIT(&ma->avail_list); + + /** 5th: initialize the first object and last guard object. */ + 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->area_base = ptr; + ma->guard_hdr = guard_hdr; + + /** 5.1: hook the first object to both obj_list and avail_list. */ + TAILQ_INSERT_TAIL(&ma->obj_list, hdr, obj_next); + TAILQ_INSERT_TAIL(&ma->avail_list, hdr, avail_next); + memarea_set_cookie(hdr, 0); + + /** 5.2: hook the guard object to only obj_list. */ + 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); + memarea_set_cookie(guard_hdr, 3); + + return ma; +} + +void +rte_memarea_destroy(struct rte_memarea *ma) +{ + if (ma == NULL) { + rte_errno = EINVAL; + 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..e2921ad83b --- /dev/null +++ b/lib/memarea/rte_memarea.h @@ -0,0 +1,137 @@ +/* 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_xxx family. + * 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. + * + * - The address returned by the allocator is align to 8B. + * + * @note The current implementation is a minimum set and does not support + * multiple-process. + */ + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RTE_MEMAREA_NAMESIZE 64 + +/** + * Memarea memory source. + */ +enum rte_memarea_source { + /** Memory source comes from rte_malloc_xxx 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. */ + /** Total size (bytes) of memarea, it should not be less be 1024. */ + size_t total_sz; + /** Indicates whether the memarea API should be MT-safe. */ + uint32_t mt_safe : 1; + union { + /** The initialization parameters if the source is set to be + * RTE_MEMAREA_SOURCE_HEAP. + */ + struct { + /** Socket from which to apply for memarea's memory. */ + int socket_id; + } heap; + /** The initialization parameters if the source is set to be + * RTE_MEMAREA_SOURCE_MEMAREA. + */ + struct { + /** Source memarea which to apply for this memarea's + * memory from. + */ + 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 (the rte_errno is set). + */ +__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. + * + * @note The rte_errno is set if destroy failed. + */ +__rte_experimental +void rte_memarea_destroy(struct rte_memarea *ma); + +#ifdef __cplusplus +} +#endif + +#endif /* RTE_MEMAREA_H */ diff --git a/lib/memarea/version.map b/lib/memarea/version.map new file mode 100644 index 0000000000..f36a04d7cf --- /dev/null +++ b/lib/memarea/version.map @@ -0,0 +1,12 @@ +EXPERIMENTAL { + global: + + rte_memarea_create; + rte_memarea_destroy; + + local: *; +}; + +INTERNAL { + local: *; +}; diff --git a/lib/meson.build b/lib/meson.build index fac2f52cad..36821e7007 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -42,6 +42,7 @@ libraries = [ 'kni', 'latencystats', 'lpm', + 'memarea', 'member', 'pcapng', 'power', -- 2.17.1