DPDK patches and discussions
 help / color / mirror / Atom feed
* [RFC] memarea: introduce memory area library
@ 2022-07-21  4:46 Chengwen Feng
  2022-07-21 15:22 ` Stephen Hemminger
                   ` (23 more replies)
  0 siblings, 24 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-07-21  4:46 UTC (permalink / raw)
  To: thomas; +Cc: dev

The memarea library is an allocator of variable-size object. It is a
collection of allocated objects that can be efficiently alloc or free
all at once, the main feature are as follows:
a) it facilitate alloc and free of memory with low overhead.

b) it provides refcnt feature which could be useful in some scenes.

c) it supports MT-safe as long as it's specified at creation time.

d) it's memory source could comes from:
d.1) system API: malloc in C library.
d.2) user provided address: it can be from the rte_malloc API series
or extended memory as long as it is available.
d.3) user provided memarea: it can be from another memarea.

Note:
a) the memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) the eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 lib/memarea/meson.build   |  10 ++
 lib/memarea/rte_memarea.c |  52 ++++++++++
 lib/memarea/rte_memarea.h | 205 ++++++++++++++++++++++++++++++++++++++
 lib/memarea/version.map   |  16 +++
 lib/meson.build           |   1 +
 5 files changed, 284 insertions(+)
 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/lib/memarea/meson.build b/lib/memarea/meson.build
new file mode 100644
index 0000000000..d5c2c442f5
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2022 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..2989b56c60
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 202 HiSilicon Limited
+ */
+
+#include <rte_common.h>
+#include <rte_memarea.h>
+
+struct rte_memarea *
+rte_memarea_create(const struct rte_memarea_param *init)
+{
+	RTE_SET_USED(init);
+	return NULL;
+}
+
+int
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+	RTE_SET_USED(ma);
+	return 0;
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, uint32_t size)
+{
+	RTE_SET_USED(ma);
+	RTE_SET_USED(size);
+	return NULL;
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	RTE_SET_USED(ma);
+	RTE_SET_USED(ptr);
+}
+
+int
+rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int16_t value)
+{
+	RTE_SET_USED(ma);
+	RTE_SET_USED(ptr);
+	RTE_SET_USED(value);
+	return 0;
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f)
+{
+	RTE_SET_USED(ma);
+	RTE_SET_USED(f);
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
new file mode 100644
index 0000000000..dfbe845595
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,205 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef RTE_MEMAREA_H
+#define RTE_MEMAREA_H
+
+/**
+ * @file
+ * RTE Memarea.
+ *
+ * A memory area is an allocator of variable-size object. It is identified
+ * by its name.
+ *
+ * The memarea is a collection of allocated objects that can be efficiently
+ * alloc or free all at once, the main feature are as follows:
+ *   a) it facilitate alloc and free of memory with low overhead.
+ *   b) it provides refcnt feature which could be useful in some scenes.
+ *   c) it supports MT-safe as long as it's specified at creation time.
+ *   d) it's memory source could comes from:
+ *      d.1) system API: malloc in C library.
+ *      d.2) user provided address: it can be from the rte_malloc API series
+ *           or extended memory as long as it is available.
+ *      d.3) user provided memarea: it can be from another memarea. So we can
+ *           build the following memory management structure:
+ *                         memarea-1
+ *                             |
+ *                             v
+ *                 ------------------------
+ *                 |           |          |
+ *                 v           v          v
+ *              memarea-2   memarea-3    obj
+ *
+ */
+
+#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 sys api (e.g. malloc) */
+	RTE_MEMAREA_SOURCE_SYSAPI,
+	/** Memory source comes from user-provided address */
+	RTE_MEMAREA_SOURCE_USER_ADDR,
+	/** Memory source comes from user-provided memarea */
+	RTE_MEMAREA_SOURCE_USER_MEMAREA,
+};
+
+struct rte_memarea;
+
+struct rte_memarea_param {
+	char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea */
+	enum rte_memarea_source source;  /**< Memory source of memarea */
+	uint64_t size;                   /**< Size (byte) of memarea */
+	uint32_t align;                  /**< Align of allocated object */
+	/** Indicates whether the memarea should be MT-safe */
+	bool mt_safe;
+	/** Indicates whether the memarea is visible to multiple process.
+	 * If the memory source is RTE_MEMAREA_SOURCE_USER_ADDR, this filed
+	 * depends on user settings and must be set.
+	 * If the memory source is RTE_MEMAREA_SOURCE_SYSAPI or
+	 * RTE_MEMAREA_SOURCE_USER_MEMAREA, this filed does not need to be set.
+	 */
+	bool mp_visible;
+	/** User provided address, this field is valid only when source
+	 * is set to RTE_MEMAREA_SOURCE_USER_ADDR.
+	 */
+	void *user_addr;
+	/** User provided memarea, this field is valid only when source
+	 * is set to RTE_MEMAREA_SOURCE_MEMAREA.
+	 */
+	struct rte_memarea *user_memarea;
+};
+
+struct rte_memarea {
+	void *private_data; /**< private management data pointer*/
+	struct rte_memarea_param init;
+};
+
+/**
+ * @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.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_destroy(struct rte_memarea *ma);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory region from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ *
+ * @return
+ *   Non-NULL on success. Otherwise NULL is returned.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, uint32_t size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory region to the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory region which need be freed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Update memory's refcnt.
+ *
+ * Update one memory region's refcnt.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory region which need be updated refcnt.
+ * @param value
+ *   The value which need be updated.
+ *   Note: it could be negative.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int16_t value);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea,
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f);
+
+#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..6d75f9f7c4
--- /dev/null
+++ b/lib/memarea/version.map
@@ -0,0 +1,16 @@
+EXPERIMENTAL {
+	global:
+
+	rte_memarea_alloc;
+	rte_memarea_create;
+	rte_memarea_destroy;
+	rte_memarea_dump;
+	rte_memarea_free;
+	rte_memarea_refcnt_update;
+
+	local: *;
+};
+
+INTERNAL {
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index c648f7d800..521a25d6c0 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.33.0


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [RFC] memarea: introduce memory area library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
@ 2022-07-21 15:22 ` Stephen Hemminger
  2022-08-04  6:36 ` Jerin Jacob
                   ` (22 subsequent siblings)
  23 siblings, 0 replies; 222+ messages in thread
From: Stephen Hemminger @ 2022-07-21 15:22 UTC (permalink / raw)
  To: Chengwen Feng; +Cc: thomas, dev

On Thu, 21 Jul 2022 12:46:48 +0800
Chengwen Feng <fengchengwen@huawei.com> wrote:

> +struct rte_memarea {
> +	void *private_data; /**< private management data pointer*/
> +	struct rte_memarea_param init;
> +};

Why does this structure have to be exposed in user API?
Hiding it in implementation would reduce ABI breakage problems.

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [RFC] memarea: introduce memory area library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
  2022-07-21 15:22 ` Stephen Hemminger
@ 2022-08-04  6:36 ` Jerin Jacob
  2022-08-30 12:41 ` Dmitry Kozlyuk
                   ` (21 subsequent siblings)
  23 siblings, 0 replies; 222+ messages in thread
From: Jerin Jacob @ 2022-08-04  6:36 UTC (permalink / raw)
  To: Chengwen Feng; +Cc: Thomas Monjalon, dpdk-dev

On Thu, Jul 21, 2022 at 10:23 AM Chengwen Feng <fengchengwen@huawei.com> wrote:
>
> The memarea library is an allocator of variable-size object. It is a
> collection of allocated objects that can be efficiently alloc or free
> all at once, the main feature are as follows:
> a) it facilitate alloc and free of memory with low overhead.
>
> b) it provides refcnt feature which could be useful in some scenes.
>
> c) it supports MT-safe as long as it's specified at creation time.
>
> d) it's memory source could comes from:
> d.1) system API: malloc in C library.
> d.2) user provided address: it can be from the rte_malloc API series
> or extended memory as long as it is available.
> d.3) user provided memarea: it can be from another memarea.
>
> Note:
> a) the memarea is oriented towards the application layer, which could
> provides 'region-based memory management' [1] function.
> b) the eal library also provide memory zone/heap management, but these
> are tied to huge pages management.
>
> [1] https://en.wikipedia.org/wiki/Region-based_memory_management
>

Looks like a good feature to add to DPDK .


> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> ---
>  lib/memarea/meson.build   |  10 ++
>  lib/memarea/rte_memarea.c |  52 ++++++++++
>  lib/memarea/rte_memarea.h | 205 ++++++++++++++++++++++++++++++++++++++
>  lib/memarea/version.map   |  16 +++
>  lib/meson.build           |   1 +
>  5 files changed, 284 insertions(+)
>  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/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
> new file mode 100644
> index 0000000000..dfbe845595
> --- /dev/null
> +++ b/lib/memarea/rte_memarea.h
> @@ -0,0 +1,205 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2022 HiSilicon Limited
> + */
> +
> +#ifndef RTE_MEMAREA_H
> +#define RTE_MEMAREA_H
> +
> +/**
> + * @file
> + * RTE Memarea.
> + *
> + * A memory area is an allocator of variable-size object. It is identified
> + * by its name.
> + *
> + * The memarea is a collection of allocated objects that can be efficiently
> + * alloc or free all at once, the main feature are as follows:
> + *   a) it facilitate alloc and free of memory with low overhead.
> + *   b) it provides refcnt feature which could be useful in some scenes.
> + *   c) it supports MT-safe as long as it's specified at creation time.
> + *   d) it's memory source could comes from:
> + *      d.1) system API: malloc in C library.
> + *      d.2) user provided address: it can be from the rte_malloc API series
> + *           or extended memory as long as it is available.
> + *      d.3) user provided memarea: it can be from another memarea. So we can
> + *           build the following memory management structure:
> + *                         memarea-1
> + *                             |
> + *                             v
> + *                 ------------------------
> + *                 |           |          |
> + *                 v           v          v
> + *              memarea-2   memarea-3    obj
> + *
> + */
> +
> +#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 sys api (e.g. malloc) */
> +       RTE_MEMAREA_SOURCE_SYSAPI,
> +       /** Memory source comes from user-provided address */
> +       RTE_MEMAREA_SOURCE_USER_ADDR,
> +       /** Memory source comes from user-provided memarea */
> +       RTE_MEMAREA_SOURCE_USER_MEMAREA,
> +};
> +
> +struct rte_memarea;
> +
> +struct rte_memarea_param {
> +       char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea */
> +       enum rte_memarea_source source;  /**< Memory source of memarea */
> +       uint64_t size;                   /**< Size (byte) of memarea */
> +       uint32_t align;                  /**< Align of allocated object */
> +       /** Indicates whether the memarea should be MT-safe */
> +       bool mt_safe;
> +       /** Indicates whether the memarea is visible to multiple process.
> +        * If the memory source is RTE_MEMAREA_SOURCE_USER_ADDR, this filed
> +        * depends on user settings and must be set.
> +        * If the memory source is RTE_MEMAREA_SOURCE_SYSAPI or
> +        * RTE_MEMAREA_SOURCE_USER_MEMAREA, this filed does not need to be set.
> +        */
> +       bool mp_visible;
> +       /** User provided address, this field is valid only when source
> +        * is set to RTE_MEMAREA_SOURCE_USER_ADDR.
> +        */
> +       void *user_addr;
> +       /** User provided memarea, this field is valid only when source
> +        * is set to RTE_MEMAREA_SOURCE_MEMAREA.
> +        */
> +       struct rte_memarea *user_memarea;

Above two can be in union as it is  based on enum rte_memarea_source.

> +};
> +
> +struct rte_memarea {
> +       void *private_data; /**< private management data pointer*/
> +       struct rte_memarea_param init;
> +};


Make it opaque to application.

Also, In Terms of features, good to have following items

1)Stats
2)Detects the memory leak.

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [RFC] memarea: introduce memory area library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
  2022-07-21 15:22 ` Stephen Hemminger
  2022-08-04  6:36 ` Jerin Jacob
@ 2022-08-30 12:41 ` Dmitry Kozlyuk
  2022-09-20  3:46 ` [PATCH 0/8] introduce memarea library Chengwen Feng
                   ` (20 subsequent siblings)
  23 siblings, 0 replies; 222+ messages in thread
From: Dmitry Kozlyuk @ 2022-08-30 12:41 UTC (permalink / raw)
  To: Chengwen Feng; +Cc: Thomas Monjalon, dpdk-dev

[-- Attachment #1: Type: text/plain, Size: 4369 bytes --]

>
> Note:
> a) the memarea is oriented towards the application layer, which could
> provides 'region-based memory management' [1] function.
>

Judging from the API, this library would rather provide
an interface to a generic allocator over a fixed memory extent,
because it offers freeing of specific elements, and thus must track them.
So it's more than RBMM. Is this intended?
It's a very interesting RFC anyway, just trying to understand the scope.

b) the eal library also provide memory zone/heap management, but these
> are tied to huge pages management.
>
[...]
> + * The memarea is a collection of allocated objects that can be
> efficiently
> + * alloc or free all at once, the main feature are as follows:
> + *   a) it facilitate alloc and free of memory with low overhead.
> + *   [...]
>
+ *   c) it supports MT-safe as long as it's specified at creation time.
>

These two bullets seem to add the most value compared to DPDK heap API.
DPDK heap overhead is at least 64 bytes per allocation (sizeof malloc_elem),
so I assume memarea aims at a large number of small elements.


> +struct rte_memarea_param {
> +       char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea */
> +       enum rte_memarea_source source;  /**< Memory source of memarea */
> +       uint64_t size;                   /**< Size (byte) of memarea */
>

Is it an upper limit or a guaranteed size?
It probably depends on the source: guaranteed for USER_ADDR,
upper limit for SYSAPI (or it would be no different from USER_ADDR),
not sure about USER_MEMAREA.

Do you envision memarea as always limited?
Generic allocators usually have means of adding extents,
even if this one doesn't currently.

Nit: size is uint64_t here but uint32_t in rte_memarea_allloc().
Should be size_t in both places.


> +       uint32_t align;                  /**< Align of allocated object */
>
+       /** Indicates whether the memarea should be MT-safe */
> +       bool mt_safe;
> +       /** Indicates whether the memarea is visible to multiple process.
> +        * If the memory source is RTE_MEMAREA_SOURCE_USER_ADDR, this filed
> +        * depends on user settings and must be set.
> +        * If the memory source is RTE_MEMAREA_SOURCE_SYSAPI or
> +        * RTE_MEMAREA_SOURCE_USER_MEMAREA, this filed does not need to be
> set.
> +        */
> +       bool mp_visible;
> +       /** User provided address, this field is valid only when source
> +        * is set to RTE_MEMAREA_SOURCE_USER_ADDR.
> +        */
> +       void *user_addr;
> +       /** User provided memarea, this field is valid only when source
> +        * is set to RTE_MEMAREA_SOURCE_MEMAREA.
> +        */
> +       struct rte_memarea *user_memarea;
>

Jerin already suggested a union here.
I'll add another reason to do so: if in the future there will be new
memarea types
that require new options, one pointer-sized field can be used to pass
anything
without breaking the ABI once this structure becomes stable.


> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Update memory's refcnt.
> + *
> + * Update one memory region's refcnt.
> + *
> + * @param ma
> + *   The pointer of memarea.
> + * @param ptr
> + *   The pointer of memory region which need be updated refcnt.
> + * @param value
> + *   The value which need be updated.
> + *   Note: it could be negative.
> + *
> + * @return
> + *   0 on success. Otherwise negative value is returned.
> + */
> +__rte_experimental
> +int rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int16_t
> value);
>

If this function only updates the refcnt, an API to inspect the refcnt is
missing.
Furthermore, in this case refcnt is just a value attached to every object,
what is the benefit compared to simply storing it in the object?

If this function also frees "ptr" when refcnt reaches zero,
missing is a way for the user to know that it did.
What happens if refcnt > 1 on rte_memarea_free()?

I don't think refcnt belongs to this library.
A principal objection: memarea is for freeing all objects at once,
refcnt is for releasing objects one-by-one when they're not used.
Technical issues I foresee: refcnt can be atomic (and require alignment) or
not,
16 bits may be too few (rte_flow_action_handle ref'd by thousands of
rte_flow).
Refcnt could be optional per memarea, but it seems like another
complication.

[-- Attachment #2: Type: text/html, Size: 6663 bytes --]

^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH 0/8] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (2 preceding siblings ...)
  2022-08-30 12:41 ` Dmitry Kozlyuk
@ 2022-09-20  3:46 ` Chengwen Feng
  2022-09-20  3:46   ` [PATCH 1/8] memarea: introduce memory area library Chengwen Feng
                     ` (7 more replies)
  2022-09-21  3:12 ` [PATCH v2 0/9] introduce memarea library Chengwen Feng
                   ` (19 subsequent siblings)
  23 siblings, 8 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-20  3:46 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

The memarea library is an allocator of variable-size object. It is a
collection of allocated objects that can be efficiently alloc or free
all at once, the main feature are as follows:
a) it facilitate alloc and free of memory with low overhead.

b) it provides refcnt feature which could be useful in some scenes.

c) it supports MT-safe as long as it's specified at creation time.

d) it's memory source could comes from:
d.1) system API: malloc in C library.
d.2) user provided address: it can be from the rte_malloc API series
or extended memory as long as it is available.
d.3) user provided memarea: it can be from another memarea.

e) it provides backup memory mechanism, the memarea object could use
another memarea object as a backup.

Note:
a) the memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) the eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>

Chengwen Feng (8):
  memarea: introduce memory area library
  test/memarea: support memarea test
  memarea: support alloc/free/update-refcnt API
  test/memarea: support alloc/free/update-refcnt test
  memarea: support dump API
  test/memarea: support dump test
  memarea: support backup memory mechanism
  test/memarea: support backup memory test

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 340 ++++++++++++++++++++++
 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  |  57 ++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  35 +++
 lib/memarea/meson.build                |  16 +
 lib/memarea/rte_memarea.c              | 385 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 214 ++++++++++++++
 lib/memarea/version.map                |  16 +
 lib/meson.build                        |   1 +
 15 files changed, 1083 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH 1/8] memarea: introduce memory area library
  2022-09-20  3:46 ` [PATCH 0/8] introduce memarea library Chengwen Feng
@ 2022-09-20  3:46   ` Chengwen Feng
  2022-09-20 11:30     ` Dmitry Kozlyuk
  2022-09-20  3:46   ` [PATCH 2/8] test/memarea: support memarea test Chengwen Feng
                     ` (6 subsequent siblings)
  7 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2022-09-20  3:46 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

The memarea library is an allocator of variable-size object. It is a
collection of allocated objects that can be efficiently alloc or free
all at once, the main features are as follows:
a) it facilitate alloc and free of memory with low overhead.

b) it provides refcnt feature which could be useful in some scenes.

c) it supports MT-safe as long as it's specified at creation time.

d) it's memory source could comes from:
d.1) system API: malloc in C library.
d.2) user provided address: it can be from the rte_malloc API series
or extended memory as long as it is available.
d.3) user provided memarea: it can be from another memarea.

This patch provides create/destroy API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.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  |  41 ++++++++
 doc/guides/rel_notes/release_22_11.rst |   6 ++
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  30 ++++++
 lib/memarea/meson.build                |  16 +++
 lib/memarea/rte_memarea.c              | 140 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 131 +++++++++++++++++++++++
 lib/memarea/version.map                |  12 +++
 lib/meson.build                        |   1 +
 13 files changed, 387 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 32ffdd1a61..92e77f1318 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1557,6 +1557,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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 186a258be4..25dbef0b68 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -64,7 +64,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 608494a7c0..bdc5d2b0d5 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -55,6 +55,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 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..1979f0a12c
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -0,0 +1,41 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2022 HiSilicon Limited
+
+Memarea Library
+===============
+
+Introduction
+------------
+
+The memarea library provides an allocator of variable-size objects, it is
+oriented towards the application layer, which could provides 'region-based
+memory management' function [1].
+
+The main features are as follows:
+
+* It facilitate alloc and free of memory with low overhead.
+
+* It's memory source could comes from: 1) System API: e.g. malloc/memalign in
+  C library. 2) User provided address: it can be from the rte_malloc API series
+  or extended memory as long as it is available. 3) User provided memarea: it
+  can be from another memarea.
+
+* The default aligement size is ``RTE_CACHE_LINE_SIZE``.
+
+* It provides refcnt feature which could be useful in some scenes.
+
+* 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 object, 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 object.
+
+Reference
+---------
+
+[1] https://en.wikipedia.org/wiki/Region-based_memory_management
diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
index 8c021cf050..1cf522132c 100644
--- a/doc/guides/rel_notes/release_22_11.rst
+++ b/doc/guides/rel_notes/release_22_11.rst
@@ -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, which could provides 'region-based memory
+  management' function.
+
 
 Removed Items
 -------------
diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
index 25ce42cdfc..708f3a39dd 100644
--- a/lib/eal/include/rte_log.h
+++ b/lib/eal/include/rte_log.h
@@ -48,6 +48,7 @@ extern "C" {
 #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
 #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
 #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
+#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..94931b46e9
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef MEMAREA_PRIVATE_H
+#define MEMAREA_PRIVATE_H
+
+#include <rte_memarea.h>
+
+#define MEMAREA_FREE_ELEM_COOKIE	0xFFFFFFFF
+
+struct memarea_elem {
+	size_t   size;
+	uint32_t cookie;
+	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
+	TAILQ_ENTRY(memarea_elem) elem_node;
+	TAILQ_ENTRY(memarea_elem) free_node;
+} __rte_cache_aligned;
+
+TAILQ_HEAD(memarea_elem_list, memarea_elem);
+
+struct memarea_private {
+	struct rte_memarea_param init;
+	rte_spinlock_t           lock;
+	void                    *area_addr;
+	struct memarea_elem_list elem_list;
+	struct memarea_elem_list free_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..0a74fb4cd1
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2022 HiSilicon Limited
+
+if is_windows
+    build = false
+    reason = 'not supported on Windows'
+    subdir_done()
+endif
+
+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..3f535d315f
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#include <stdio.h>
+
+#include <rte_common.h>
+#include <rte_log.h>
+#include <rte_malloc.h>
+#include <rte_memarea.h>
+#include <rte_spinlock.h>
+
+#include "memarea_private.h"
+
+static int
+memarea_check_param(const struct rte_memarea_param *init)
+{
+	size_t len;
+
+	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
+	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea name invalid!\n");
+		return -EINVAL;
+	}
+
+	if (init->source >= RTE_MEMAREA_SOURCE_BUTT) {
+		RTE_LOG(ERR, MEMAREA, "memarea source invalid!\n");
+		return -EINVAL;
+	}
+
+	if (init->total_sz <= sizeof(struct memarea_elem)) {
+		RTE_LOG(ERR, MEMAREA, "memarea total size invalid!\n");
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR && init->user_addr == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea user provided addr invalid!\n");
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR &&
+	    ((uintptr_t)init->user_addr & (RTE_CACHE_LINE_SIZE - 1))) {
+		RTE_LOG(ERR, MEMAREA, "memarea user provided addr align invalid!\n");
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA && init->user_memarea == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea user provided memarea invalid!\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+memarea_alloc_from_system_api(size_t size)
+{
+	void *ptr = NULL;
+	int ret;
+
+	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+	if (ret)
+		return NULL;
+	return ptr;
+}
+
+static void *
+memarea_alloc_area(const struct rte_memarea_param *init)
+{
+	void *ptr = NULL;
+
+	if (init->source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		ptr = memarea_alloc_from_system_api(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
+		ptr = init->user_addr;
+
+	if (ptr == NULL)
+		RTE_LOG(ERR, MEMAREA, "memarea alloc memory area fail!\n");
+
+	return ptr;
+}
+
+struct rte_memarea *
+rte_memarea_create(const struct rte_memarea_param *init)
+{
+	struct memarea_private *priv;
+	struct memarea_elem *elem;
+	struct rte_memarea *ma;
+	void *addr;
+	int ret;
+
+	ret = memarea_check_param(init);
+	if (ret)
+		return NULL;
+
+	addr = memarea_alloc_area(init);
+	if (addr == NULL)
+		return NULL;
+
+	ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
+	priv = rte_zmalloc(NULL, sizeof(struct memarea_private), RTE_CACHE_LINE_SIZE);
+	if (ma == NULL || priv == NULL) {
+		RTE_LOG(ERR, MEMAREA, "malloc memarea management obj fail!\n");
+		rte_free(ma);
+		rte_free(priv);
+		return NULL;
+	}
+
+	ma->private_data = priv;
+	priv->init = *init;
+	rte_spinlock_init(&priv->lock);
+	TAILQ_INIT(&priv->elem_list);
+	TAILQ_INIT(&priv->free_list);
+	priv->area_addr = addr;
+	elem = addr;
+	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	elem->refcnt = 0;
+	TAILQ_INSERT_TAIL(&priv->elem_list, elem, elem_node);
+	TAILQ_INSERT_TAIL(&priv->free_list, elem, free_node);
+
+	return ma;
+}
+
+static void
+memarea_free_area(struct memarea_private *priv)
+{
+	if (priv->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		free(priv->area_addr);
+}
+
+void
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+	if (ma == NULL)
+		return;
+	memarea_free_area(ma->private_data);
+	rte_free(ma->private_data);
+	rte_free(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
new file mode 100644
index 0000000000..ddca0a47e2
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef RTE_MEMAREA_H
+#define RTE_MEMAREA_H
+
+/**
+ * @file
+ * RTE Memarea.
+ *
+ * A memory area is an allocator of variable-size object. It is identified
+ * by its name.
+ *
+ * The memarea is a collection of allocated objects that can be efficiently
+ * alloc or free all at once, the main feature are as follows:
+ *   a) It facilitate alloc and free of memory with low overhead.
+ *   b) It's memory source could comes from:
+ *      1) System API: malloc/memalign in C library.
+ *      2) User provided address: it can be from the rte_malloc API series
+ *         or extended memory as long as it is available. The address must be
+ *         aligned to RTE_CACHE_LINE_SIZE.
+ *      3) User provided memarea: it can be from another memarea. So we can
+ *         build the following memory management structure:
+ *         \code{.unparsed}
+ *                           -------------
+ *                           | memarea-1 |
+ *                           -------------
+ *                                 |
+ *                                 v
+ *                  ------------------------------
+ *                  |               |            |
+ *                  v               v            v
+ *            -------------   -------------   -------
+ *            | memarea-2 |   | memarea-3 |   | obj |
+ *            -------------   -------------   -------
+ *         \endcode
+ *   c) The default alignment size is RTE_CACHE_LINE_SIZE.
+ *   d) It provides refcnt feature which could be useful in some scenes.
+ *   e) 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 object.
+ */
+
+#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 system API (e.g. malloc). */
+	RTE_MEMAREA_SOURCE_SYSTEM_API,
+	/** Memory source comes from user-provided address. */
+	RTE_MEMAREA_SOURCE_USER_ADDR,
+	/** Memory source comes from user-provided memarea. */
+	RTE_MEMAREA_SOURCE_USER_MEMAREA,
+
+	RTE_MEMAREA_SOURCE_BUTT
+};
+
+struct rte_memarea {
+	void *private_data; /**< private management data pointer. */
+};
+
+struct rte_memarea_param {
+	char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
+	enum rte_memarea_source source;  /**< Memory source of memarea. */
+	size_t total_sz;                 /**< total size (bytes) of memarea. */
+	/** Indicates whether the memarea API should be MT-safe. */
+	uint32_t mt_safe : 1;
+	union {
+		/** User provided address, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER_ADDR.
+		 * Note: the provided address must align at least
+		 * RTE_CACHE_LINE_SIZE.
+		 */
+		void *user_addr;
+		/** User provided memarea, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER_MEMAREA.
+		 */
+		struct rte_memarea *user_memarea;
+	};
+};
+
+/**
+ * @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 */
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 c648f7d800..521a25d6c0 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH 2/8] test/memarea: support memarea test
  2022-09-20  3:46 ` [PATCH 0/8] introduce memarea library Chengwen Feng
  2022-09-20  3:46   ` [PATCH 1/8] memarea: introduce memory area library Chengwen Feng
@ 2022-09-20  3:46   ` Chengwen Feng
  2022-09-20  3:46   ` [PATCH 3/8] memarea: support alloc/free/update-refcnt API Chengwen Feng
                     ` (5 subsequent siblings)
  7 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-20  3:46 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch support memarea test about API rte_memarea_create and
rte_memarea_destroy.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 130 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 92e77f1318..e051829906 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1561,6 +1561,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index bf1d81f84a..ad4e0baee5 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -83,6 +83,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -198,6 +199,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..3b03b9f06c
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#ifdef RTE_EXEC_ENV_WINDOWS
+static int
+test_memarea(void)
+{
+	printf("memarea not supported on Windows, skipping test\n");
+	return TEST_SKIPPED;
+}
+
+#else
+
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) \
+			printf("%s Failed\n", #test_func); \
+		else \
+			printf("%s Passed\n", #test_func); \
+	} while (0)
+
+static void
+test_memarea_init_def_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "test-memarea");
+	init->source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int i;
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_BUTT;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_def_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address align invalid */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	for (i = 1; i < RTE_CACHE_LINE_SIZE; i++) {
+		init.user_addr = (void *)((uintptr_t)i);
+		ma = rte_memarea_create(&init);
+		RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	}
+
+	/* test for user memarea NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	struct rte_memarea_param init;
+	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea *ma;
+
+	/* test for create with system API */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with user-address */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	init.user_addr = (void *)(((uintptr_t)user_buffer + RTE_CACHE_LINE_SIZE) &
+				  ~(RTE_CACHE_LINE_SIZE - 1));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	return 0;
+}
+
+#endif /* !RTE_EXEC_ENV_WINDOWS */
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH 3/8] memarea: support alloc/free/update-refcnt API
  2022-09-20  3:46 ` [PATCH 0/8] introduce memarea library Chengwen Feng
  2022-09-20  3:46   ` [PATCH 1/8] memarea: introduce memory area library Chengwen Feng
  2022-09-20  3:46   ` [PATCH 2/8] test/memarea: support memarea test Chengwen Feng
@ 2022-09-20  3:46   ` Chengwen Feng
  2022-09-20  3:46   ` [PATCH 4/8] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
                     ` (4 subsequent siblings)
  7 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-20  3:46 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  10 ++
 lib/memarea/memarea_private.h         |   3 +
 lib/memarea/rte_memarea.c             | 140 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  56 +++++++++++
 lib/memarea/version.map               |   3 +
 5 files changed, 212 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 1979f0a12c..967129d560 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -35,6 +35,16 @@ failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea object.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory region from
+the memarea object.
+
+The ``rte_memarea_free()`` function is used to free one memory region which
+allocated by ``rte_memarea_alloc()``.
+
+The ``rte_memarea_update_refcnt()`` function is used to update the memory
+region's reference count, if the count reaches zero, the memory region will
+be freed to memarea object.
+
 Reference
 ---------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 94931b46e9..f71e01e7b5 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -25,6 +25,9 @@ struct memarea_private {
 	void                    *area_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
+
+	uint64_t alloc_fails;
+	uint64_t refcnt_check_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 3f535d315f..fe4a820173 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -3,6 +3,7 @@
  */
 
 #include <stdio.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -73,6 +74,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 		ptr = memarea_alloc_from_system_api(init->total_sz);
 	else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
 		ptr = init->user_addr;
+	else if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		ptr = rte_memarea_alloc(init->user_memarea, init->total_sz, 0);
 
 	if (ptr == NULL)
 		RTE_LOG(ERR, MEMAREA, "memarea alloc memory area fail!\n");
@@ -127,6 +130,8 @@ memarea_free_area(struct memarea_private *priv)
 {
 	if (priv->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
 		free(priv->area_addr);
+	else if (priv->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		rte_memarea_free(priv->init.user_memarea, priv->area_addr);
 }
 
 void
@@ -138,3 +143,138 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	rte_free(ma->private_data);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct memarea_private *priv)
+{
+	if (priv->init.mt_safe)
+		rte_spinlock_lock(&priv->lock);
+}
+
+static inline void
+memarea_unlock(struct memarea_private *priv)
+{
+	if (priv->init.mt_safe)
+		rte_spinlock_unlock(&priv->lock);
+}
+
+#define roundup(val, align) ((((val) + ((align) - 1)) / (align)) * (align))
+
+static inline bool
+memarea_whether_add_node(size_t free_size, size_t need_size)
+{
+	size_t align_size = roundup(need_size, RTE_CACHE_LINE_SIZE);
+	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
+}
+
+static inline void
+memarea_add_node(struct memarea_private *priv, struct memarea_elem *elem, size_t need_size)
+{
+	size_t align_size = roundup(need_size, RTE_CACHE_LINE_SIZE);
+	struct memarea_elem *new_elem;
+	new_elem = (struct memarea_elem *)((uintptr_t)elem + sizeof(struct memarea_elem) +
+					   align_size);
+	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	new_elem->refcnt = 0;
+	TAILQ_INSERT_AFTER(&priv->elem_list, elem, new_elem, elem_node);
+	TAILQ_INSERT_AFTER(&priv->free_list, elem, new_elem, free_node);
+	elem->size = align_size;
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	struct memarea_private *priv;
+	struct memarea_elem *elem;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0))
+		return NULL;
+
+	priv = ma->private_data;
+	memarea_lock(priv);
+	TAILQ_FOREACH(elem, &priv->free_list, free_node) {
+		if (elem->size < size)
+			continue;
+		if (memarea_whether_add_node(elem->size, size))
+			memarea_add_node(priv, elem, size);
+		elem->cookie = cookie;
+		elem->refcnt = 1;
+		TAILQ_REMOVE(&priv->free_list, elem, free_node);
+		ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));
+		break;
+	}
+	if (unlikely(ptr == NULL))
+		priv->alloc_fails++;
+	memarea_unlock(priv);
+
+	return ptr;
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	rte_memarea_update_refcnt(ma, ptr, -1);
+}
+
+static inline void
+memarea_merge_node(struct memarea_private *priv, struct memarea_elem *curr,
+		   struct memarea_elem *next, bool del_next_from_free,
+		   bool add_curr_to_free)
+{
+	curr->size += next->size + sizeof(struct memarea_elem);
+	next->size = 0;
+	next->cookie = 0;
+	TAILQ_REMOVE(&priv->elem_list, next, elem_node);
+	if (del_next_from_free)
+		TAILQ_REMOVE(&priv->free_list, next, free_node);
+	if (add_curr_to_free)
+		TAILQ_INSERT_TAIL(&priv->free_list, curr, free_node);
+}
+
+static inline void
+memarea_free_elem(struct memarea_private *priv, struct memarea_elem *elem)
+{
+	struct memarea_elem *prev, *next;
+	bool merged = false;
+	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
+	next = TAILQ_NEXT(elem, elem_node);
+	if (prev != NULL && prev->refcnt == 0) {
+		memarea_merge_node(priv, prev, elem, false, false);
+		elem = prev;
+		merged = true;
+	}
+	if (next != NULL && next->refcnt == 0) {
+		memarea_merge_node(priv, elem, next, true, !merged);
+		merged = true;
+	}
+	if (!merged) {
+		elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&priv->free_list, elem, free_node);
+	}
+}
+
+void
+rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
+{
+	struct memarea_elem *elem = (struct memarea_elem *)((uintptr_t)ptr -
+							    sizeof(struct memarea_elem));
+	struct memarea_private *priv = ma->private_data;
+
+	memarea_lock(priv);
+	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
+		RTE_LOG(ERR, MEMAREA,
+			"memarea cookie: %u curr refcnt: %d update refcnt: %d check fail!\n",
+			elem->cookie, elem->refcnt, value);
+		priv->refcnt_check_fails++;
+		if (elem->refcnt > 0)
+			elem->refcnt += value;
+		memarea_unlock(priv);
+		return;
+	}
+	elem->refcnt += value;
+	if (elem->refcnt == 0)
+		memarea_free_elem(priv, elem);
+	memarea_unlock(priv);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index ddca0a47e2..cded2904e3 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -124,6 +124,62 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory region from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ * @param cookie
+ *   User-provided footprint which could used to debug memory leak problem.
+ *
+ * @return
+ *   Non-NULL on success. Otherwise NULL is returned.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory region to the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory region which need be freed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Update memory's refcnt.
+ *
+ * Update one memory region's refcnt.
+ * When refcnt is updated to be zero, the memory region is freed.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory region which need be updated refcnt.
+ * @param value
+ *   The value which need be updated.
+ */
+__rte_experimental
+void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..a0026fc5f9 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,11 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
+	rte_memarea_update_refcnt;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH 4/8] test/memarea: support alloc/free/update-refcnt test
  2022-09-20  3:46 ` [PATCH 0/8] introduce memarea library Chengwen Feng
                     ` (2 preceding siblings ...)
  2022-09-20  3:46   ` [PATCH 3/8] memarea: support alloc/free/update-refcnt API Chengwen Feng
@ 2022-09-20  3:46   ` Chengwen Feng
  2022-09-20  3:46   ` [PATCH 5/8] memarea: support dump API Chengwen Feng
                     ` (3 subsequent siblings)
  7 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-20  3:46 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 138 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 137 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 3b03b9f06c..66616be0b6 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -40,6 +40,12 @@ test_memarea_init_def_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_alloced_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -94,9 +100,9 @@ test_memarea_create_bad_param(void)
 static int
 test_memarea_create_destroy(void)
 {
+	struct rte_memarea *ma, *user_ma;
 	struct rte_memarea_param init;
 	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
-	struct rte_memarea *ma;
 
 	/* test for create with system API */
 	test_memarea_init_def_param(&init);
@@ -114,6 +120,133 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with user-memarea */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	user_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(user_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.user_memarea = user_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(user_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_alloced_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_alloced_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test free with refcnt fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_alloced_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	/* test update refcnt with fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_alloced_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_update_refcnt(ma, ptr, -2);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[ALLOC_MAX_NUM];
+	int i;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1, 0);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, ptr[6]);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -122,6 +255,9 @@ test_memarea(void)
 {
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH 5/8] memarea: support dump API
  2022-09-20  3:46 ` [PATCH 0/8] introduce memarea library Chengwen Feng
                     ` (3 preceding siblings ...)
  2022-09-20  3:46   ` [PATCH 4/8] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
@ 2022-09-20  3:46   ` Chengwen Feng
  2022-09-20  3:46   ` [PATCH 6/8] test/memarea: support dump test Chengwen Feng
                     ` (2 subsequent siblings)
  7 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-20  3:46 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +
 lib/memarea/rte_memarea.c             | 83 +++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 21 +++++++
 lib/memarea/version.map               |  1 +
 4 files changed, 108 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 967129d560..7686625c90 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -45,6 +45,9 @@ The ``rte_memarea_update_refcnt()`` function is used to update the memory
 region's reference count, if the count reaches zero, the memory region will
 be freed to memarea object.
 
+The ``rte_memarea_dump()`` function is used to dump the internal information
+of a memarea object.
+
 Reference
 ---------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index fe4a820173..e40715d16a 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2022 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <sys/queue.h>
 
@@ -278,3 +279,85 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		memarea_free_elem(priv, elem);
 	memarea_unlock(priv);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		return "system-api";
+	else if (source == RTE_MEMAREA_SOURCE_USER_ADDR)
+		return "user-addr";
+	else if (source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		return "user-memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_name(struct rte_memarea *ma)
+{
+	struct memarea_private *priv = ma->private_data;
+	return priv->init.name;
+}
+
+static uint32_t
+memarea_elem_list_num(struct memarea_private *priv)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &priv->elem_list, elem_node)
+		num++;
+
+	return num;
+}
+
+static uint32_t
+memarea_free_list_num(struct memarea_private *priv)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &priv->free_list, free_node)
+		num++;
+
+	return num;
+}
+
+static void
+memarea_dump_all(struct memarea_private *priv, FILE *f)
+{
+	struct memarea_elem *elem;
+
+	fprintf(f, "  regions:\n");
+	TAILQ_FOREACH(elem, &priv->elem_list, elem_node)
+		fprintf(f, "    size: 0x%lx cookie: %u refcnt: %d\n",
+			elem->size, elem->cookie, elem->refcnt);
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	struct memarea_private *priv;
+
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	priv = ma->private_data;
+	memarea_lock(priv);
+	fprintf(f, "memarea name: %s\n", priv->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(priv->init.source));
+	if (priv->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		fprintf(f, "  source-user-memarea: %s\n", memarea_name(priv->init.user_memarea));
+	fprintf(f, "  total-size: 0x%lx\n", priv->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", priv->init.mt_safe ? "yes" : "no");
+	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(priv));
+	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(priv));
+	fprintf(f, "  alloc_fails: %" PRIu64 "\n", priv->alloc_fails);
+	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", priv->refcnt_check_fails);
+	if (dump_all)
+		memarea_dump_all(priv, f);
+	memarea_unlock(priv);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index cded2904e3..d1383f18c3 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -180,6 +180,27 @@ void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 __rte_experimental
 void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory region information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index a0026fc5f9..d8ddd93c13 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 	rte_memarea_update_refcnt;
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH 6/8] test/memarea: support dump test
  2022-09-20  3:46 ` [PATCH 0/8] introduce memarea library Chengwen Feng
                     ` (4 preceding siblings ...)
  2022-09-20  3:46   ` [PATCH 5/8] memarea: support dump API Chengwen Feng
@ 2022-09-20  3:46   ` Chengwen Feng
  2022-09-20  3:46   ` [PATCH 7/8] memarea: support backup memory mechanism Chengwen Feng
  2022-09-20  3:46   ` [PATCH 8/8] test/memarea: support backup memory test Chengwen Feng
  7 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-20  3:46 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports rte_memarea_dump() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 66616be0b6..a1214f7080 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -250,6 +250,38 @@ test_memarea_alloc_free(void)
 	return 0;
 }
 
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -258,6 +290,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH 7/8] memarea: support backup memory mechanism
  2022-09-20  3:46 ` [PATCH 0/8] introduce memarea library Chengwen Feng
                     ` (5 preceding siblings ...)
  2022-09-20  3:46   ` [PATCH 6/8] test/memarea: support dump test Chengwen Feng
@ 2022-09-20  3:46   ` Chengwen Feng
  2022-09-20  3:46   ` [PATCH 8/8] test/memarea: support backup memory test Chengwen Feng
  7 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-20  3:46 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports backup memory mechanism, the memarea object could
use another memarea object as a backup.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +++
 lib/memarea/memarea_private.h         |  2 ++
 lib/memarea/rte_memarea.c             | 22 ++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  6 ++++++
 4 files changed, 33 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 7686625c90..c52ab95aeb 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -26,6 +26,9 @@ The main features are as follows:
 
 * It supports MT-safe as long as it's specified at creation time.
 
+* It provides backup memory mechanism, the memory object could use another
+  memarea object as a backup.
+
 Library API Overview
 --------------------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index f71e01e7b5..ce765ed801 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -23,11 +23,13 @@ struct memarea_private {
 	struct rte_memarea_param init;
 	rte_spinlock_t           lock;
 	void                    *area_addr;
+	void                    *top_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
 
 	uint64_t alloc_fails;
 	uint64_t refcnt_check_fails;
+	uint64_t bak_alloc_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index e40715d16a..3efe8d4819 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -116,6 +116,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
 	TAILQ_INIT(&priv->elem_list);
 	TAILQ_INIT(&priv->free_list);
 	priv->area_addr = addr;
+	priv->top_addr = (void *)((uintptr_t)addr + init->total_sz - 1);
 	elem = addr;
 	elem->size = init->total_sz - sizeof(struct memarea_elem);
 	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
@@ -183,6 +184,15 @@ memarea_add_node(struct memarea_private *priv, struct memarea_elem *elem, size_t
 	elem->size = align_size;
 }
 
+static inline void *
+memarea_alloc_backup(struct memarea_private *priv, size_t size, uint32_t cookie)
+{
+	void *ptr = rte_memarea_alloc(priv->init.bak_memarea, size, cookie);
+	if (unlikely(ptr == NULL))
+		priv->bak_alloc_fails++;
+	return ptr;
+}
+
 void *
 rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 {
@@ -206,6 +216,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 		ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));
 		break;
 	}
+	if (ptr == NULL && priv->init.bak_memarea != NULL)
+		ptr = memarea_alloc_backup(priv, size, cookie);
 	if (unlikely(ptr == NULL))
 		priv->alloc_fails++;
 	memarea_unlock(priv);
@@ -264,6 +276,12 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 	struct memarea_private *priv = ma->private_data;
 
 	memarea_lock(priv);
+	if (ptr < priv->area_addr || ptr > priv->top_addr) {
+		rte_memarea_update_refcnt(priv->init.bak_memarea, ptr, value);
+		memarea_unlock(priv);
+		return;
+	}
+
 	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
 		RTE_LOG(ERR, MEMAREA,
 			"memarea cookie: %u curr refcnt: %d update refcnt: %d check fail!\n",
@@ -274,6 +292,7 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		memarea_unlock(priv);
 		return;
 	}
+
 	elem->refcnt += value;
 	if (elem->refcnt == 0)
 		memarea_free_elem(priv, elem);
@@ -351,10 +370,13 @@ rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
 		fprintf(f, "  source-user-memarea: %s\n", memarea_name(priv->init.user_memarea));
 	fprintf(f, "  total-size: 0x%lx\n", priv->init.total_sz);
 	fprintf(f, "  mt-safe: %s\n", priv->init.mt_safe ? "yes" : "no");
+	if (priv->init.bak_memarea)
+		fprintf(f, "  backup-memarea-name: %s\n", memarea_name(priv->init.bak_memarea));
 	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(priv));
 	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(priv));
 	fprintf(f, "  alloc_fails: %" PRIu64 "\n", priv->alloc_fails);
 	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", priv->refcnt_check_fails);
+	fprintf(f, "  backup_alloc_fails: %" PRIu64 "\n", priv->bak_alloc_fails);
 	if (dump_all)
 		memarea_dump_all(priv, f);
 	memarea_unlock(priv);
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index d1383f18c3..a467627523 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -41,6 +41,8 @@
  *      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 object.
+ *   f) It provides backup memory mechanism, the memarea object could use
+ *      another memarea object as a backup.
  */
 
 #include <stdbool.h>
@@ -91,6 +93,10 @@ struct rte_memarea_param {
 		 */
 		struct rte_memarea *user_memarea;
 	};
+	/** Backup memarea, which is used to handle the scenario where the
+	 * current memarea object allocation failure.
+	 */
+	struct rte_memarea *bak_memarea;
 };
 
 /**
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH 8/8] test/memarea: support backup memory test
  2022-09-20  3:46 ` [PATCH 0/8] introduce memarea library Chengwen Feng
                     ` (6 preceding siblings ...)
  2022-09-20  3:46   ` [PATCH 7/8] memarea: support backup memory mechanism Chengwen Feng
@ 2022-09-20  3:46   ` Chengwen Feng
  7 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-20  3:46 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports backup memory mechanism test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index a1214f7080..1f7ab3fb1f 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -282,6 +282,46 @@ test_memarea_dump(void)
 	return 0;
 }
 
+static int
+test_memarea_backup(void)
+{
+	struct rte_memarea *ma, *bak_ma;
+	struct rte_memarea_param init;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	strcat(init.name, "_backup");
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	bak_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(bak_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE >> 2;
+	init.bak_memarea = bak_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for backup */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 3, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+	rte_memarea_free(ma, ptr);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(bak_ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -291,6 +331,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
+	MEMAREA_TEST_API_RUN(test_memarea_backup);
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH 1/8] memarea: introduce memory area library
  2022-09-20  3:46   ` [PATCH 1/8] memarea: introduce memory area library Chengwen Feng
@ 2022-09-20 11:30     ` Dmitry Kozlyuk
  2022-09-20 11:31       ` Dmitry Kozlyuk
  2022-09-20 12:06       ` fengchengwen
  0 siblings, 2 replies; 222+ messages in thread
From: Dmitry Kozlyuk @ 2022-09-20 11:30 UTC (permalink / raw)
  To: Chengwen Feng; +Cc: thomas, dev, stephen, jerinjacobk

2022-09-20 03:46 (UTC+0000), Chengwen Feng:
> The memarea library is an allocator of variable-size object. It is a
> collection of allocated objects that can be efficiently alloc or free
> all at once, the main features are as follows:
> a) it facilitate alloc and free of memory with low overhead.

Yet, the overhead is 64B per element, just like rte_malloc.

> b) it provides refcnt feature which could be useful in some scenes.

Are you sure refcnt should be in this library?
I've expressed my concerns here:

https://inbox.dpdk.org/dev/CAEYuUWBpC-9dCqKJ0LZi6RkCUwyeYEghLRBMBUBtUx4Ljg+pAQ@mail.gmail.com

There are more unanswered questions in that mail,
it would be good to clarify them before reviewing these patches
in order to understand all the intentions.

> +static int
> +memarea_check_param(const struct rte_memarea_param *init)
> +{
> +	size_t len;
> +
> +	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
> +	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
> +		RTE_LOG(ERR, MEMAREA, "memarea name invalid!\n");
> +		return -EINVAL;
> +	}

Please check init->name == NULL first.

> +struct rte_memarea *
> +rte_memarea_create(const struct rte_memarea_param *init)
> +{
[...]
> +		RTE_LOG(ERR, MEMAREA, "malloc memarea management obj fail!\n");

In all error messages, it would be useful to provide details:
the name of the area, what size was requested, etc.

> +/**
> + * Memarea memory source.
> + */
> +enum rte_memarea_source {
> +	/** Memory source comes from system API (e.g. malloc). */
> +	RTE_MEMAREA_SOURCE_SYSTEM_API,
> +	/** Memory source comes from user-provided address. */
> +	RTE_MEMAREA_SOURCE_USER_ADDR,
> +	/** Memory source comes from user-provided memarea. */
> +	RTE_MEMAREA_SOURCE_USER_MEMAREA,
> +
> +	RTE_MEMAREA_SOURCE_BUTT

DPDK enumerations must not include an item to hold the element count,
because it is harmful for ABI (e.g. developers create arrays of this size
and when a new item is added in a new DPDK version, the array overflows).

If it's supposed to mean "the end of item list",
the proper word would be "last" or "max" BTW :)

> +};
> +
> +struct rte_memarea {
> +	void *private_data; /**< private management data pointer. */
> +};

Jerin and Stephen suggested to make the structure opaque,
i.e. only declare the struct and define it privately.
It would reduce ABI and simplify allocation.
Any justification to expose it?

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH 1/8] memarea: introduce memory area library
  2022-09-20 11:30     ` Dmitry Kozlyuk
@ 2022-09-20 11:31       ` Dmitry Kozlyuk
  2022-09-20 12:06       ` fengchengwen
  1 sibling, 0 replies; 222+ messages in thread
From: Dmitry Kozlyuk @ 2022-09-20 11:31 UTC (permalink / raw)
  To: Chengwen Feng; +Cc: thomas, dev, stephen, jerinjacobk

2022-09-20 14:30 (UTC+0300), Dmitry Kozlyuk:
> 2022-09-20 03:46 (UTC+0000), Chengwen Feng:
> > The memarea library is an allocator of variable-size object. It is a
> > collection of allocated objects that can be efficiently alloc or free
> > all at once, the main features are as follows:
> > a) it facilitate alloc and free of memory with low overhead.  
> 
> Yet, the overhead is 64B per element, just like rte_malloc.

Disregard this one, rte_malloc overhead is 128B (2 cache lines).

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH 1/8] memarea: introduce memory area library
  2022-09-20 11:30     ` Dmitry Kozlyuk
  2022-09-20 11:31       ` Dmitry Kozlyuk
@ 2022-09-20 12:06       ` fengchengwen
  1 sibling, 0 replies; 222+ messages in thread
From: fengchengwen @ 2022-09-20 12:06 UTC (permalink / raw)
  To: Dmitry Kozlyuk; +Cc: thomas, dev, stephen, jerinjacobk

Hi Dmitry,

On 2022/9/20 19:30, Dmitry Kozlyuk wrote:
> 2022-09-20 03:46 (UTC+0000), Chengwen Feng:
>> The memarea library is an allocator of variable-size object. It is a
>> collection of allocated objects that can be efficiently alloc or free
>> all at once, the main features are as follows:
>> a) it facilitate alloc and free of memory with low overhead.
> Yet, the overhead is 64B per element, just like rte_malloc.
>
>> b) it provides refcnt feature which could be useful in some scenes.
> Are you sure refcnt should be in this library?
> I've expressed my concerns here:
>
> https://inbox.dpdk.org/dev/CAEYuUWBpC-9dCqKJ0LZi6RkCUwyeYEghLRBMBUBtUx4Ljg+pAQ@mail.gmail.com
>
> There are more unanswered questions in that mail,
> it would be good to clarify them before reviewing these patches
> in order to understand all the intentions.

Sorry to forgot reply it.

We have the following scene which used refcnt:

     nic-rx  ->  decoder ->  process

                                    |

                                   ->  recording

as above show, the process and recording module both use the decoder's 
output, the are just reader.

so in this case, the refcnt is useful.

>
>> +static int
>> +memarea_check_param(const struct rte_memarea_param *init)
>> +{
>> +	size_t len;
>> +
>> +	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
>> +	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
>> +		RTE_LOG(ERR, MEMAREA, "memarea name invalid!\n");
>> +		return -EINVAL;
>> +	}
> Please check init->name == NULL first.

No need checking because name is an array.

Maybe I should check init == NULL here.

>
>> +struct rte_memarea *
>> +rte_memarea_create(const struct rte_memarea_param *init)
>> +{
> [...]
>> +		RTE_LOG(ERR, MEMAREA, "malloc memarea management obj fail!\n");
> In all error messages, it would be useful to provide details:
> the name of the area, what size was requested, etc.

will fix in v2.

>
>> +/**
>> + * Memarea memory source.
>> + */
>> +enum rte_memarea_source {
>> +	/** Memory source comes from system API (e.g. malloc). */
>> +	RTE_MEMAREA_SOURCE_SYSTEM_API,
>> +	/** Memory source comes from user-provided address. */
>> +	RTE_MEMAREA_SOURCE_USER_ADDR,
>> +	/** Memory source comes from user-provided memarea. */
>> +	RTE_MEMAREA_SOURCE_USER_MEMAREA,
>> +
>> +	RTE_MEMAREA_SOURCE_BUTT
> DPDK enumerations must not include an item to hold the element count,
> because it is harmful for ABI (e.g. developers create arrays of this size
> and when a new item is added in a new DPDK version, the array overflows).
>
> If it's supposed to mean "the end of item list",
> the proper word would be "last" or "max" BTW :)
will fix in v2
>
>> +};
>> +
>> +struct rte_memarea {
>> +	void *private_data; /**< private management data pointer. */
>> +};
> Jerin and Stephen suggested to make the structure opaque,
> i.e. only declare the struct and define it privately.
> It would reduce ABI and simplify allocation.
> Any justification to expose it?

do you mean the rte_memarea just void * ? it just (void 
*)(memarea_private *)priv ?

It's another popular type to impl ABI compatiable.

It's more simpler, will fix in v2


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v2 0/9] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (3 preceding siblings ...)
  2022-09-20  3:46 ` [PATCH 0/8] introduce memarea library Chengwen Feng
@ 2022-09-21  3:12 ` Chengwen Feng
  2022-09-21  3:12   ` [PATCH v2 1/9] memarea: introduce memory area library Chengwen Feng
                     ` (8 more replies)
  2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
                   ` (18 subsequent siblings)
  23 siblings, 9 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-21  3:12 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

The memarea library is an allocator of variable-size object. It is a
collection of allocated objects that can be efficiently alloc or free
all at once, the main feature are as follows:
a) it facilitate alloc and free of memory with low overhead.

b) it provides refcnt feature which could be useful in some scenes.

c) it supports MT-safe as long as it's specified at creation time.

d) it's memory source could comes from:
d.1) system API: malloc in C library.
d.2) user provided address: it can be from the rte_malloc API series
or extended memory as long as it is available.
d.3) user provided memarea: it can be from another memarea.

e) it provides backup memory mechanism, the memarea object could use
another memarea object as a backup.

Note:
a) the memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) the eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>

Chengwen Feng (9):
  memarea: introduce memory area library
  test/memarea: support memarea test
  memarea: support alloc/free/update-refcnt API
  test/memarea: support alloc/free/update-refcnt test
  memarea: support dump API
  test/memarea: support dump test
  memarea: support backup memory mechanism
  test/memarea: support backup memory test
  test/memarea: support no MT-safe test

---
v2: 
* fix compile issues reported by dpdk-test-report
* address Dimitry and Jerin's comments
* add no MT-safe test

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 379 +++++++++++++++++++++++++
 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  |  57 ++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  35 +++
 lib/memarea/meson.build                |  16 ++
 lib/memarea/rte_memarea.c              | 379 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 210 ++++++++++++++
 lib/memarea/version.map                |  16 ++
 lib/meson.build                        |   1 +
 15 files changed, 1112 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v2 1/9] memarea: introduce memory area library
  2022-09-21  3:12 ` [PATCH v2 0/9] introduce memarea library Chengwen Feng
@ 2022-09-21  3:12   ` Chengwen Feng
  2022-09-21  3:12   ` [PATCH v2 2/9] test/memarea: support memarea test Chengwen Feng
                     ` (7 subsequent siblings)
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-21  3:12 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

The memarea library is an allocator of variable-size object. It is a
collection of allocated objects that can be efficiently alloc or free
all at once, the main features are as follows:
a) it facilitate alloc and free of memory with low overhead.

b) it provides refcnt feature which could be useful in some scenes.

c) it supports MT-safe as long as it's specified at creation time.

d) it's memory source could comes from:
d.1) system API: malloc in C library.
d.2) user provided address: it can be from the rte_malloc API series
or extended memory as long as it is available.
d.3) user provided memarea: it can be from another memarea.

This patch provides create/destroy API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.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  |  41 +++++++
 doc/guides/rel_notes/release_22_11.rst |   6 ++
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  30 ++++++
 lib/memarea/meson.build                |  16 +++
 lib/memarea/rte_memarea.c              | 144 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 127 ++++++++++++++++++++++
 lib/memarea/version.map                |  12 +++
 lib/meson.build                        |   1 +
 13 files changed, 387 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 26d3a7077c..4d5bf986c6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1558,6 +1558,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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 186a258be4..25dbef0b68 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -64,7 +64,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 608494a7c0..bdc5d2b0d5 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -55,6 +55,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 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..1979f0a12c
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -0,0 +1,41 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2022 HiSilicon Limited
+
+Memarea Library
+===============
+
+Introduction
+------------
+
+The memarea library provides an allocator of variable-size objects, it is
+oriented towards the application layer, which could provides 'region-based
+memory management' function [1].
+
+The main features are as follows:
+
+* It facilitate alloc and free of memory with low overhead.
+
+* It's memory source could comes from: 1) System API: e.g. malloc/memalign in
+  C library. 2) User provided address: it can be from the rte_malloc API series
+  or extended memory as long as it is available. 3) User provided memarea: it
+  can be from another memarea.
+
+* The default aligement size is ``RTE_CACHE_LINE_SIZE``.
+
+* It provides refcnt feature which could be useful in some scenes.
+
+* 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 object, 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 object.
+
+Reference
+---------
+
+[1] https://en.wikipedia.org/wiki/Region-based_memory_management
diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
index 8c021cf050..1cf522132c 100644
--- a/doc/guides/rel_notes/release_22_11.rst
+++ b/doc/guides/rel_notes/release_22_11.rst
@@ -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, which could provides 'region-based memory
+  management' function.
+
 
 Removed Items
 -------------
diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
index 25ce42cdfc..708f3a39dd 100644
--- a/lib/eal/include/rte_log.h
+++ b/lib/eal/include/rte_log.h
@@ -48,6 +48,7 @@ extern "C" {
 #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
 #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
 #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
+#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..c76392d3e6
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef MEMAREA_PRIVATE_H
+#define MEMAREA_PRIVATE_H
+
+#include <rte_memarea.h>
+
+#define MEMAREA_FREE_ELEM_COOKIE	0xFFFFFFFF
+
+struct memarea_elem {
+	size_t   size;
+	uint32_t cookie;
+	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
+	TAILQ_ENTRY(memarea_elem) elem_node;
+	TAILQ_ENTRY(memarea_elem) free_node;
+} __rte_cache_aligned;
+
+TAILQ_HEAD(memarea_elem_list, memarea_elem);
+
+struct rte_memarea {
+	struct rte_memarea_param init;
+	rte_spinlock_t           lock;
+	void                    *area_addr;
+	struct memarea_elem_list elem_list;
+	struct memarea_elem_list free_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..0a74fb4cd1
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2022 HiSilicon Limited
+
+if is_windows
+    build = false
+    reason = 'not supported on Windows'
+    subdir_done()
+endif
+
+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..6e918dfaf9
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#include <stdio.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"
+
+static int
+memarea_check_param(const struct rte_memarea_param *init)
+{
+	size_t len;
+
+	if (init == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
+		return -EINVAL;
+	}
+
+	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
+	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
+		return -EINVAL;
+	}
+
+	if (init->source != RTE_MEMAREA_SOURCE_SYSTEM_API &&
+	    init->source != RTE_MEMAREA_SOURCE_USER_ADDR &&
+	    init->source != RTE_MEMAREA_SOURCE_USER_MEMAREA) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
+			init->name, init->source);
+		return -EINVAL;
+	}
+
+	if (init->total_sz <= sizeof(struct memarea_elem)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
+			init->name, init->total_sz);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR && init->user_addr == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR &&
+	    ((uintptr_t)init->user_addr & (RTE_CACHE_LINE_SIZE - 1))) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr should align: %d!\n",
+			init->name, RTE_CACHE_LINE_SIZE);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA && init->user_memarea == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided memarea is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+memarea_alloc_from_system_api(size_t size)
+{
+	void *ptr = NULL;
+	int ret;
+
+	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+	if (ret)
+		return NULL;
+	return ptr;
+}
+
+static void *
+memarea_alloc_area(const struct rte_memarea_param *init)
+{
+	void *ptr = NULL;
+
+	if (init->source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		ptr = memarea_alloc_from_system_api(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
+		ptr = init->user_addr;
+
+	if (ptr == NULL)
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
+
+	return ptr;
+}
+
+struct rte_memarea *
+rte_memarea_create(const struct rte_memarea_param *init)
+{
+	struct memarea_elem *elem;
+	struct rte_memarea *ma;
+	void *addr;
+	int ret;
+
+	ret = memarea_check_param(init);
+	if (ret)
+		return NULL;
+
+	addr = memarea_alloc_area(init);
+	if (addr == NULL)
+		return NULL;
+
+	ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
+	if (ma == NULL) {
+		RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
+		return NULL;
+	}
+
+	ma->init = *init;
+	rte_spinlock_init(&ma->lock);
+	TAILQ_INIT(&ma->elem_list);
+	TAILQ_INIT(&ma->free_list);
+	ma->area_addr = addr;
+	elem = addr;
+	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	elem->refcnt = 0;
+	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
+	TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+
+	return ma;
+}
+
+static void
+memarea_free_area(struct rte_memarea *ma)
+{
+	if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		free(ma->area_addr);
+}
+
+void
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+	if (ma == NULL)
+		return;
+	memarea_free_area(ma);
+	rte_free(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
new file mode 100644
index 0000000000..15f6fabcb5
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef RTE_MEMAREA_H
+#define RTE_MEMAREA_H
+
+/**
+ * @file
+ * RTE Memarea.
+ *
+ * A memory area is an allocator of variable-size object. It is identified
+ * by its name.
+ *
+ * The memarea is a collection of allocated objects that can be efficiently
+ * alloc or free all at once, the main feature are as follows:
+ *   a) It facilitate alloc and free of memory with low overhead.
+ *   b) It's memory source could comes from:
+ *      1) System API: malloc/memalign in C library.
+ *      2) User provided address: it can be from the rte_malloc API series
+ *         or extended memory as long as it is available. The address must be
+ *         aligned to RTE_CACHE_LINE_SIZE.
+ *      3) User provided memarea: it can be from another memarea. So we can
+ *         build the following memory management structure:
+ *         \code{.unparsed}
+ *                           -------------
+ *                           | memarea-1 |
+ *                           -------------
+ *                                 |
+ *                                 v
+ *                  ------------------------------
+ *                  |               |            |
+ *                  v               v            v
+ *            -------------   -------------   -------
+ *            | memarea-2 |   | memarea-3 |   | obj |
+ *            -------------   -------------   -------
+ *         \endcode
+ *   c) The default alignment size is RTE_CACHE_LINE_SIZE.
+ *   d) It provides refcnt feature which could be useful in some scenes.
+ *   e) 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 object.
+ */
+
+#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 system API (e.g. malloc). */
+	RTE_MEMAREA_SOURCE_SYSTEM_API,
+	/** Memory source comes from user-provided address. */
+	RTE_MEMAREA_SOURCE_USER_ADDR,
+	/** Memory source comes from user-provided memarea. */
+	RTE_MEMAREA_SOURCE_USER_MEMAREA,
+};
+
+struct rte_memarea;
+
+struct rte_memarea_param {
+	char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
+	enum rte_memarea_source source;  /**< Memory source of memarea. */
+	size_t total_sz;                 /**< total size (bytes) of memarea. */
+	/** Indicates whether the memarea API should be MT-safe. */
+	uint32_t mt_safe : 1;
+	union {
+		/** User provided address, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER_ADDR.
+		 * Note: the provided address must align at least
+		 * RTE_CACHE_LINE_SIZE.
+		 */
+		void *user_addr;
+		/** User provided memarea, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER_MEMAREA.
+		 */
+		struct rte_memarea *user_memarea;
+	};
+};
+
+/**
+ * @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 */
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 c648f7d800..521a25d6c0 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v2 2/9] test/memarea: support memarea test
  2022-09-21  3:12 ` [PATCH v2 0/9] introduce memarea library Chengwen Feng
  2022-09-21  3:12   ` [PATCH v2 1/9] memarea: introduce memory area library Chengwen Feng
@ 2022-09-21  3:12   ` Chengwen Feng
  2022-09-21  3:12   ` [PATCH v2 3/9] memarea: support alloc/free/update-refcnt API Chengwen Feng
                     ` (6 subsequent siblings)
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-21  3:12 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch support memarea test about API rte_memarea_create and
rte_memarea_destroy.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 134 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 137 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 4d5bf986c6..eb2de32884 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1562,6 +1562,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index d5cad72116..778de8d65d 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -84,6 +84,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -199,6 +200,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..b2dbc71a50
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#ifdef RTE_EXEC_ENV_WINDOWS
+static int
+test_memarea(void)
+{
+	printf("memarea not supported on Windows, skipping test\n");
+	return TEST_SKIPPED;
+}
+
+#else
+
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) \
+			printf("%s Failed\n", #test_func); \
+		else \
+			printf("%s Passed\n", #test_func); \
+	} while (0)
+
+static void
+test_memarea_init_def_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "test-memarea");
+	init->source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int i;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_def_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address align invalid */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	for (i = 1; i < RTE_CACHE_LINE_SIZE; i++) {
+		init.user_addr = (void *)((uintptr_t)i);
+		ma = rte_memarea_create(&init);
+		RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	}
+
+	/* test for user memarea NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	struct rte_memarea_param init;
+	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea *ma;
+
+	/* test for create with system API */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with user-address */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	init.user_addr = (void *)(((uintptr_t)user_buffer + RTE_CACHE_LINE_SIZE) &
+				  ~(RTE_CACHE_LINE_SIZE - 1));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	return 0;
+}
+
+#endif /* !RTE_EXEC_ENV_WINDOWS */
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v2 3/9] memarea: support alloc/free/update-refcnt API
  2022-09-21  3:12 ` [PATCH v2 0/9] introduce memarea library Chengwen Feng
  2022-09-21  3:12   ` [PATCH v2 1/9] memarea: introduce memory area library Chengwen Feng
  2022-09-21  3:12   ` [PATCH v2 2/9] test/memarea: support memarea test Chengwen Feng
@ 2022-09-21  3:12   ` Chengwen Feng
  2022-09-21  3:12   ` [PATCH v2 4/9] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
                     ` (5 subsequent siblings)
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-21  3:12 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  10 ++
 lib/memarea/memarea_private.h         |   3 +
 lib/memarea/rte_memarea.c             | 141 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  56 ++++++++++
 lib/memarea/version.map               |   3 +
 5 files changed, 213 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 1979f0a12c..967129d560 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -35,6 +35,16 @@ failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea object.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory region from
+the memarea object.
+
+The ``rte_memarea_free()`` function is used to free one memory region which
+allocated by ``rte_memarea_alloc()``.
+
+The ``rte_memarea_update_refcnt()`` function is used to update the memory
+region's reference count, if the count reaches zero, the memory region will
+be freed to memarea object.
+
 Reference
 ---------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index c76392d3e6..98406879b9 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -25,6 +25,9 @@ struct rte_memarea {
 	void                    *area_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
+
+	uint64_t alloc_fails;
+	uint64_t refcnt_check_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 6e918dfaf9..7c85dbf491 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -3,6 +3,7 @@
  */
 
 #include <stdio.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -83,6 +84,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 		ptr = memarea_alloc_from_system_api(init->total_sz);
 	else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
 		ptr = init->user_addr;
+	else if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		ptr = rte_memarea_alloc(init->user_memarea, init->total_sz, 0);
 
 	if (ptr == NULL)
 		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
@@ -132,6 +135,8 @@ memarea_free_area(struct rte_memarea *ma)
 {
 	if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
 		free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		rte_memarea_free(ma->init.user_memarea, ma->area_addr);
 }
 
 void
@@ -142,3 +147,139 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(ma);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+#define memarea_roundup(val, align) ((((val) + ((align) - 1)) / (align)) * (align))
+
+static inline bool
+memarea_whether_add_node(size_t free_size, size_t need_size)
+{
+	size_t align_size = memarea_roundup(need_size, RTE_CACHE_LINE_SIZE);
+	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
+}
+
+static inline void
+memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
+{
+	size_t align_size = memarea_roundup(need_size, RTE_CACHE_LINE_SIZE);
+	struct memarea_elem *new_elem;
+	new_elem = (struct memarea_elem *)((uintptr_t)elem + sizeof(struct memarea_elem) +
+					   align_size);
+	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	new_elem->refcnt = 0;
+	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
+	TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
+	elem->size = align_size;
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	struct memarea_elem *elem;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0))
+		return NULL;
+
+	memarea_lock(ma);
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->size < size)
+			continue;
+		if (memarea_whether_add_node(elem->size, size))
+			memarea_add_node(ma, elem, size);
+		elem->cookie = cookie;
+		elem->refcnt = 1;
+		TAILQ_REMOVE(&ma->free_list, elem, free_node);
+		ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));
+		break;
+	}
+	if (unlikely(ptr == NULL))
+		ma->alloc_fails++;
+	memarea_unlock(ma);
+
+	return ptr;
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	rte_memarea_update_refcnt(ma, ptr, -1);
+}
+
+static inline void
+memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
+		   struct memarea_elem *next, bool del_next_from_free,
+		   bool add_curr_to_free)
+{
+	curr->size += next->size + sizeof(struct memarea_elem);
+	next->size = 0;
+	next->cookie = 0;
+	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
+	if (del_next_from_free)
+		TAILQ_REMOVE(&ma->free_list, next, free_node);
+	if (add_curr_to_free)
+		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
+}
+
+static inline void
+memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
+{
+	struct memarea_elem *prev, *next;
+	bool merged = false;
+	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
+	next = TAILQ_NEXT(elem, elem_node);
+	if (prev != NULL && prev->refcnt == 0) {
+		memarea_merge_node(ma, prev, elem, false, false);
+		elem = prev;
+		merged = true;
+	}
+	if (next != NULL && next->refcnt == 0) {
+		memarea_merge_node(ma, elem, next, true, !merged);
+		merged = true;
+	}
+	if (!merged) {
+		elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	}
+}
+
+void
+rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
+{
+	struct memarea_elem *elem = (struct memarea_elem *)((uintptr_t)ptr -
+							    sizeof(struct memarea_elem));
+
+	if (unlikely(ma == NULL || ptr == NULL))
+		return;
+
+	memarea_lock(ma);
+	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
+		RTE_LOG(ERR, MEMAREA,
+			"memarea: %s cookie: %u curr refcnt: %d update refcnt: %d check fail!\n",
+			ma->init.name, elem->cookie, elem->refcnt, value);
+		ma->refcnt_check_fails++;
+		if (elem->refcnt > 0)
+			elem->refcnt += value;
+		memarea_unlock(ma);
+		return;
+	}
+
+	elem->refcnt += value;
+	if (elem->refcnt == 0)
+		memarea_free_elem(ma, elem);
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 15f6fabcb5..0ad195c4f9 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -120,6 +120,62 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory region from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ * @param cookie
+ *   User-provided footprint which could used to debug memory leak problem.
+ *
+ * @return
+ *   Non-NULL on success. Otherwise NULL is returned.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory region to the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory region which need be freed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Update memory's refcnt.
+ *
+ * Update one memory region's refcnt.
+ * When refcnt is updated to be zero, the memory region is freed.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory region which need be updated refcnt.
+ * @param value
+ *   The value which need be updated.
+ */
+__rte_experimental
+void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..a0026fc5f9 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,11 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
+	rte_memarea_update_refcnt;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v2 4/9] test/memarea: support alloc/free/update-refcnt test
  2022-09-21  3:12 ` [PATCH v2 0/9] introduce memarea library Chengwen Feng
                     ` (2 preceding siblings ...)
  2022-09-21  3:12   ` [PATCH v2 3/9] memarea: support alloc/free/update-refcnt API Chengwen Feng
@ 2022-09-21  3:12   ` Chengwen Feng
  2022-09-21  3:12   ` [PATCH v2 5/9] memarea: support dump API Chengwen Feng
                     ` (4 subsequent siblings)
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-21  3:12 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 143 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 142 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index b2dbc71a50..866b86165f 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -40,6 +40,12 @@ test_memarea_init_def_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_allocated_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -98,9 +104,9 @@ test_memarea_create_bad_param(void)
 static int
 test_memarea_create_destroy(void)
 {
+	struct rte_memarea *ma, *user_ma;
 	struct rte_memarea_param init;
 	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
-	struct rte_memarea *ma;
 
 	/* test for create with system API */
 	test_memarea_init_def_param(&init);
@@ -118,6 +124,138 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with user-memarea */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	user_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(user_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.user_memarea = user_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(user_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test invalid parameters with update-refcnt */
+	rte_memarea_update_refcnt(NULL, (void *)(uintptr_t)1, 0);
+	rte_memarea_update_refcnt(ma, NULL, 0);
+	rte_memarea_update_refcnt(NULL, NULL, 0);
+
+	/* test free with refcnt fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	/* test update refcnt with fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_update_refcnt(ma, ptr, -2);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[ALLOC_MAX_NUM];
+	int i;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1, 0);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, ptr[6]);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -126,6 +264,9 @@ test_memarea(void)
 {
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v2 5/9] memarea: support dump API
  2022-09-21  3:12 ` [PATCH v2 0/9] introduce memarea library Chengwen Feng
                     ` (3 preceding siblings ...)
  2022-09-21  3:12   ` [PATCH v2 4/9] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
@ 2022-09-21  3:12   ` Chengwen Feng
  2022-09-21  3:12   ` [PATCH v2 6/9] test/memarea: support dump test Chengwen Feng
                     ` (3 subsequent siblings)
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-21  3:12 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 ++
 lib/memarea/rte_memarea.c             | 73 +++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 21 ++++++++
 lib/memarea/version.map               |  1 +
 4 files changed, 98 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 967129d560..7686625c90 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -45,6 +45,9 @@ The ``rte_memarea_update_refcnt()`` function is used to update the memory
 region's reference count, if the count reaches zero, the memory region will
 be freed to memarea object.
 
+The ``rte_memarea_dump()`` function is used to dump the internal information
+of a memarea object.
+
 Reference
 ---------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 7c85dbf491..e4d43e5907 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2022 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <sys/queue.h>
 
@@ -283,3 +284,75 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		memarea_free_elem(ma, elem);
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		return "system-api";
+	else if (source == RTE_MEMAREA_SOURCE_USER_ADDR)
+		return "user-addr";
+	else if (source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		return "user-memarea";
+	else
+		return "unknown";
+}
+
+static uint32_t
+memarea_elem_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+		num++;
+
+	return num;
+}
+
+static uint32_t
+memarea_free_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->free_list, free_node)
+		num++;
+
+	return num;
+}
+
+static void
+memarea_dump_all(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_elem *elem;
+
+	fprintf(f, "  regions:\n");
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+		fprintf(f, "    size: 0x%zx cookie: %u refcnt: %d\n",
+			elem->size, elem->cookie, elem->refcnt);
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		fprintf(f, "  source-user-memarea: %s\n", ma->init.user_memarea->init.name);
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
+	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
+	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
+	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (dump_all)
+		memarea_dump_all(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 0ad195c4f9..2a92755b63 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -176,6 +176,27 @@ void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 __rte_experimental
 void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory region information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index a0026fc5f9..d8ddd93c13 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 	rte_memarea_update_refcnt;
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v2 6/9] test/memarea: support dump test
  2022-09-21  3:12 ` [PATCH v2 0/9] introduce memarea library Chengwen Feng
                     ` (4 preceding siblings ...)
  2022-09-21  3:12   ` [PATCH v2 5/9] memarea: support dump API Chengwen Feng
@ 2022-09-21  3:12   ` Chengwen Feng
  2022-09-21  3:12   ` [PATCH v2 7/9] memarea: support backup memory mechanism Chengwen Feng
                     ` (2 subsequent siblings)
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-21  3:12 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports rte_memarea_dump() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 866b86165f..a07fb4424f 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -259,6 +259,38 @@ test_memarea_alloc_free(void)
 	return 0;
 }
 
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -267,6 +299,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v2 7/9] memarea: support backup memory mechanism
  2022-09-21  3:12 ` [PATCH v2 0/9] introduce memarea library Chengwen Feng
                     ` (5 preceding siblings ...)
  2022-09-21  3:12   ` [PATCH v2 6/9] test/memarea: support dump test Chengwen Feng
@ 2022-09-21  3:12   ` Chengwen Feng
  2022-09-21  3:12   ` [PATCH v2 8/9] test/memarea: support backup memory test Chengwen Feng
  2022-09-21  3:12   ` [PATCH v2 9/9] test/memarea: support no MT-safe test Chengwen Feng
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-21  3:12 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports backup memory mechanism, the memarea object could
use another memarea object as a backup.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +++
 lib/memarea/memarea_private.h         |  2 ++
 lib/memarea/rte_memarea.c             | 21 +++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  6 ++++++
 4 files changed, 32 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 7686625c90..c52ab95aeb 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -26,6 +26,9 @@ The main features are as follows:
 
 * It supports MT-safe as long as it's specified at creation time.
 
+* It provides backup memory mechanism, the memory object could use another
+  memarea object as a backup.
+
 Library API Overview
 --------------------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 98406879b9..08735ca81f 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -23,11 +23,13 @@ struct rte_memarea {
 	struct rte_memarea_param init;
 	rte_spinlock_t           lock;
 	void                    *area_addr;
+	void                    *top_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
 
 	uint64_t alloc_fails;
 	uint64_t refcnt_check_fails;
+	uint64_t bak_alloc_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index e4d43e5907..6cad9d5b2f 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -121,6 +121,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
 	TAILQ_INIT(&ma->elem_list);
 	TAILQ_INIT(&ma->free_list);
 	ma->area_addr = addr;
+	ma->top_addr = (void *)((uintptr_t)addr + init->total_sz - 1);
 	elem = addr;
 	elem->size = init->total_sz - sizeof(struct memarea_elem);
 	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
@@ -187,6 +188,15 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
 	elem->size = align_size;
 }
 
+static inline void *
+memarea_alloc_backup(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	void *ptr = rte_memarea_alloc(ma->init.bak_memarea, size, cookie);
+	if (unlikely(ptr == NULL))
+		ma->bak_alloc_fails++;
+	return ptr;
+}
+
 void *
 rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 {
@@ -208,6 +218,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 		ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));
 		break;
 	}
+	if (ptr == NULL && ma->init.bak_memarea != NULL)
+		ptr = memarea_alloc_backup(ma, size, cookie);
 	if (unlikely(ptr == NULL))
 		ma->alloc_fails++;
 	memarea_unlock(ma);
@@ -268,6 +280,12 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		return;
 
 	memarea_lock(ma);
+	if (ptr < ma->area_addr || ptr > ma->top_addr) {
+		rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
+		memarea_unlock(ma);
+		return;
+	}
+
 	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
 		RTE_LOG(ERR, MEMAREA,
 			"memarea: %s cookie: %u curr refcnt: %d update refcnt: %d check fail!\n",
@@ -346,10 +364,13 @@ rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
 		fprintf(f, "  source-user-memarea: %s\n", ma->init.user_memarea->init.name);
 	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
 	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	if (ma->init.bak_memarea)
+		fprintf(f, "  backup-memarea-name: %s\n", ma->init.bak_memarea->init.name);
 	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
 	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
 	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
 	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	fprintf(f, "  backup_alloc_fails: %" PRIu64 "\n", ma->bak_alloc_fails);
 	if (dump_all)
 		memarea_dump_all(ma, f);
 	memarea_unlock(ma);
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 2a92755b63..ed5e8c5db5 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -41,6 +41,8 @@
  *      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 object.
+ *   f) It provides backup memory mechanism, the memarea object could use
+ *      another memarea object as a backup.
  */
 
 #include <stdbool.h>
@@ -87,6 +89,10 @@ struct rte_memarea_param {
 		 */
 		struct rte_memarea *user_memarea;
 	};
+	/** Backup memarea, which is used to handle the scenario where the
+	 * current memarea object allocation failure.
+	 */
+	struct rte_memarea *bak_memarea;
 };
 
 /**
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v2 8/9] test/memarea: support backup memory test
  2022-09-21  3:12 ` [PATCH v2 0/9] introduce memarea library Chengwen Feng
                     ` (6 preceding siblings ...)
  2022-09-21  3:12   ` [PATCH v2 7/9] memarea: support backup memory mechanism Chengwen Feng
@ 2022-09-21  3:12   ` Chengwen Feng
  2022-09-21  3:12   ` [PATCH v2 9/9] test/memarea: support no MT-safe test Chengwen Feng
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-21  3:12 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports backup memory mechanism test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index a07fb4424f..9609909d7c 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -291,6 +291,46 @@ test_memarea_dump(void)
 	return 0;
 }
 
+static int
+test_memarea_backup(void)
+{
+	struct rte_memarea *ma, *bak_ma;
+	struct rte_memarea_param init;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	strcat(init.name, "_backup");
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	bak_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(bak_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE >> 2;
+	init.bak_memarea = bak_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for backup */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 3, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+	rte_memarea_free(ma, ptr);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(bak_ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -300,6 +340,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
+	MEMAREA_TEST_API_RUN(test_memarea_backup);
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v2 9/9] test/memarea: support no MT-safe test
  2022-09-21  3:12 ` [PATCH v2 0/9] introduce memarea library Chengwen Feng
                     ` (7 preceding siblings ...)
  2022-09-21  3:12   ` [PATCH v2 8/9] test/memarea: support backup memory test Chengwen Feng
@ 2022-09-21  3:12   ` Chengwen Feng
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-21  3:12 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

MT-safe is enabled by default in previous test, this patch adds no
MT-safe test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 9609909d7c..2ab90fb5b5 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -331,6 +331,35 @@ test_memarea_backup(void)
 	return 0;
 }
 
+static int
+test_memarea_no_mt_safe(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init.mt_safe = false;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for all API */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	rte_memarea_free(ma, rte_memarea_alloc(ma, 1, 0));
+	rte_memarea_update_refcnt(ma, rte_memarea_alloc(ma, 1, 0), 1);
+	rte_memarea_update_refcnt(ma, rte_memarea_alloc(ma, 1, 0), -1);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -341,6 +370,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
 	MEMAREA_TEST_API_RUN(test_memarea_backup);
+	MEMAREA_TEST_API_RUN(test_memarea_no_mt_safe);
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v3 00/10] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (4 preceding siblings ...)
  2022-09-21  3:12 ` [PATCH v2 0/9] introduce memarea library Chengwen Feng
@ 2022-09-24  7:49 ` Chengwen Feng
  2022-09-24  7:49   ` [PATCH v3 01/10] memarea: " Chengwen Feng
                     ` (10 more replies)
  2022-10-05  3:38 ` [PATCH v4 " datshan
                   ` (17 subsequent siblings)
  23 siblings, 11 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-24  7:49 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- The default alignment size is RTE_CACHE_LINE_SIZE.

- The memory region can be initialized from the following memory
  sources:
  1. RTE memory: e.g. invoke rte_malloc_socket to obtain.
  2. System API: e.g. invoke posix_memalign to obtain.
  3. User provided address: it can be from extended memory as long as
     it is available. The address must be aligned to
     RTE_CACHE_LINE_SIZE.
  4. User provided memarea: it can be from another memarea.

- It provides refcnt feature which could be useful in multi-reader
  scenario.

- It provides backup memory mechanism, the memarea could use another
  memarea as a backup.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>

Chengwen Feng (10):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc/free/update-refcnt API
  test/memarea: support alloc/free/update-refcnt test
  memarea: support dump API
  test/memarea: support dump test
  memarea: support backup memory mechanism
  test/memarea: support backup memory test
  memarea: detect memory corruption based on magic
  test/memarea: support no MT-safe test

---
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 401 +++++++++++++++++++++++
 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  |  55 ++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  37 +++
 lib/memarea/meson.build                |  16 +
 lib/memarea/rte_memarea.c              | 430 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 229 +++++++++++++
 lib/memarea/version.map                |  16 +
 lib/meson.build                        |   1 +
 16 files changed, 1205 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v3 01/10] memarea: introduce memarea library
  2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
@ 2022-09-24  7:49   ` Chengwen Feng
  2022-09-24  7:49   ` [PATCH v3 02/10] test/memarea: support memarea test Chengwen Feng
                     ` (9 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-24  7:49 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

The memarea library is an allocator of variable-size object which based
on a memory region.

This patch provides create/destroy API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.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  |  39 ++++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  30 +++++
 lib/memarea/meson.build                |  16 +++
 lib/memarea/rte_memarea.c              | 157 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 145 +++++++++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 14 files changed, 417 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 26d3a7077c..4d5bf986c6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1558,6 +1558,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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 186a258be4..25dbef0b68 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -64,7 +64,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 608494a7c0..bdc5d2b0d5 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -55,6 +55,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 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..b96dad15f6
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -0,0 +1,39 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2022 HiSilicon Limited
+
+Memarea Library
+===============
+
+Introduction
+------------
+
+The memarea library provides an allocator of variable-size objects, it is
+oriented towards the application layer, which could provides 'region-based
+memory management' function [1].
+
+The main features are as follows:
+
+* The default aligement size is ``RTE_CACHE_LINE_SIZE``.
+
+* The memory region can be initialized from the following memory sources:
+  a) RTE memory: e.g. invoke ``rte_malloc_socket`` to obtain. b) System API:
+  e.g. invoke posix_memalign to obtain. c) User provided address: it can be from
+  extendedd memory as long as it is available. d) User provided memarea: it can
+  be from another memarea.
+
+* It provides refcnt feature which could be useful in multi-reader scenario.
+
+* 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.
+
+Reference
+---------
+
+[1] https://en.wikipedia.org/wiki/Region-based_memory_management
diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
index 43502349bd..1625424141 100644
--- a/doc/guides/rel_notes/release_22_11.rst
+++ b/doc/guides/rel_notes/release_22_11.rst
@@ -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, which could provides 'region-based memory
+  management' function.
+
 
 Removed Items
 -------------
diff --git a/lib/eal/common/eal_common_log.c b/lib/eal/common/eal_common_log.c
index bd7b188ceb..3d62af59c6 100644
--- a/lib/eal/common/eal_common_log.c
+++ b/lib/eal/common/eal_common_log.c
@@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
 	{RTE_LOGTYPE_EFD,        "lib.efd"},
 	{RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
 	{RTE_LOGTYPE_GSO,        "lib.gso"},
+	{RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
 	{RTE_LOGTYPE_USER1,      "user1"},
 	{RTE_LOGTYPE_USER2,      "user2"},
 	{RTE_LOGTYPE_USER3,      "user3"},
diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
index 25ce42cdfc..708f3a39dd 100644
--- a/lib/eal/include/rte_log.h
+++ b/lib/eal/include/rte_log.h
@@ -48,6 +48,7 @@ extern "C" {
 #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
 #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
 #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
+#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..c76392d3e6
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef MEMAREA_PRIVATE_H
+#define MEMAREA_PRIVATE_H
+
+#include <rte_memarea.h>
+
+#define MEMAREA_FREE_ELEM_COOKIE	0xFFFFFFFF
+
+struct memarea_elem {
+	size_t   size;
+	uint32_t cookie;
+	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
+	TAILQ_ENTRY(memarea_elem) elem_node;
+	TAILQ_ENTRY(memarea_elem) free_node;
+} __rte_cache_aligned;
+
+TAILQ_HEAD(memarea_elem_list, memarea_elem);
+
+struct rte_memarea {
+	struct rte_memarea_param init;
+	rte_spinlock_t           lock;
+	void                    *area_addr;
+	struct memarea_elem_list elem_list;
+	struct memarea_elem_list free_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..0a74fb4cd1
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2022 HiSilicon Limited
+
+if is_windows
+    build = false
+    reason = 'not supported on Windows'
+    subdir_done()
+endif
+
+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..868da7661d
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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"
+
+static int
+memarea_check_param(const struct rte_memarea_param *init)
+{
+	size_t len;
+
+	if (init == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
+		return -EINVAL;
+	}
+
+	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
+	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
+		return -EINVAL;
+	}
+
+	if (init->source != RTE_MEMAREA_SOURCE_RTE_MEMORY &&
+	    init->source != RTE_MEMAREA_SOURCE_SYSTEM_API &&
+	    init->source != RTE_MEMAREA_SOURCE_USER_ADDR &&
+	    init->source != RTE_MEMAREA_SOURCE_USER_MEMAREA) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
+			init->name, init->source);
+		return -EINVAL;
+	}
+
+	if (init->total_sz <= sizeof(struct memarea_elem)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
+			init->name, init->total_sz);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR && init->user_addr == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR &&
+	    ((uintptr_t)init->user_addr & (RTE_CACHE_LINE_SIZE - 1))) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr should align: %d!\n",
+			init->name, RTE_CACHE_LINE_SIZE);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA && init->user_memarea == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided memarea is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->alg != RTE_MEMAREA_ALG_DEFAULT) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
+			init->name, init->alg);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+memarea_alloc_from_system_api(size_t size)
+{
+	void *ptr = NULL;
+	int ret;
+
+	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+	if (ret)
+		return NULL;
+	return ptr;
+}
+
+static void *
+memarea_alloc_area(const struct rte_memarea_param *init)
+{
+	void *ptr = NULL;
+
+	if (init->source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
+		ptr = rte_malloc_socket(NULL, init->total_sz, RTE_CACHE_LINE_SIZE,
+					init->numa_socket);
+	else if (init->source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		ptr = memarea_alloc_from_system_api(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
+		ptr = init->user_addr;
+
+	if (ptr == NULL)
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
+
+	return ptr;
+}
+
+struct rte_memarea *
+rte_memarea_create(const struct rte_memarea_param *init)
+{
+	struct memarea_elem *elem;
+	struct rte_memarea *ma;
+	void *addr;
+	int ret;
+
+	ret = memarea_check_param(init);
+	if (ret)
+		return NULL;
+
+	addr = memarea_alloc_area(init);
+	if (addr == NULL)
+		return NULL;
+
+	ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
+	if (ma == NULL) {
+		RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
+		return NULL;
+	}
+
+	ma->init = *init;
+	rte_spinlock_init(&ma->lock);
+	TAILQ_INIT(&ma->elem_list);
+	TAILQ_INIT(&ma->free_list);
+	ma->area_addr = addr;
+	elem = addr;
+	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	elem->refcnt = 0;
+	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
+	TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+
+	return ma;
+}
+
+static void
+memarea_free_area(struct rte_memarea *ma)
+{
+	if (ma->init.source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
+		rte_free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		free(ma->area_addr);
+}
+
+void
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+	if (ma == NULL)
+		return;
+	memarea_free_area(ma);
+	rte_free(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
new file mode 100644
index 0000000000..543cda4cac
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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 default alignment size is RTE_CACHE_LINE_SIZE.
+ * - The memory region can be initialized from the following memory sources:
+ *   1. RTE memory: e.g. invoke rte_malloc_socket to obtain.
+ *   2. System API: e.g. invoke posix_memalign to obtain.
+ *   3. User provided address: it can be from extended memory as long as it is
+ *      available. The address must be aligned to RTE_CACHE_LINE_SIZE.
+ *   4) User provided memarea: it can be from another memarea. So we can build
+ *      the following memory management structure:
+ *         \code{.unparsed}
+ *                           -------------
+ *                           | memarea-1 |
+ *                           -------------
+ *                                 |
+ *                                 v
+ *                  -------------------------------
+ *                  |               |             |
+ *                  v               v             v
+ *            -------------   -------------   ----------
+ *            | memarea-2 |   | memarea-3 |   | object |
+ *            -------------   -------------   ----------
+ *         \endcode
+ *      As shown above, the memarea-2/3 both create from memarea-1's memory.
+ * - It provides refcnt feature which could be useful in multi-reader scenario.
+ * - 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_RTE_MEMORY,
+	/** Memory source comes from system API. */
+	RTE_MEMAREA_SOURCE_SYSTEM_API,
+	/** Memory source comes from user-provided address. */
+	RTE_MEMAREA_SOURCE_USER_ADDR,
+	/** Memory source comes from user-provided memarea. */
+	RTE_MEMAREA_SOURCE_USER_MEMAREA,
+};
+
+/**
+ * Memarea memory management algorithm.
+ */
+enum rte_memarea_alg {
+	/* The default management algorithm is a variant of the next fit
+	 * algorithm. It uses a free-list to apply for memory and uses an
+	 * element-list in ascending order of address to support merging
+	 * upon free.
+	 */
+	RTE_MEMAREA_ALG_DEFAULT,
+};
+
+struct rte_memarea;
+
+struct rte_memarea_param {
+	char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
+	enum rte_memarea_source source;  /**< Memory source of memarea. */
+	enum rte_memarea_alg 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_RTE_MEMORY.
+		 */
+		int numa_socket;
+		/** User provided address, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER_ADDR.
+		 * Note: the provided address must align at least
+		 * RTE_CACHE_LINE_SIZE.
+		 */
+		void *user_addr;
+		/** User provided memarea, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER_MEMAREA.
+		 */
+		struct rte_memarea *user_memarea;
+	};
+};
+
+/**
+ * @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 */
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 c648f7d800..521a25d6c0 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v3 02/10] test/memarea: support memarea test
  2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
  2022-09-24  7:49   ` [PATCH v3 01/10] memarea: " Chengwen Feng
@ 2022-09-24  7:49   ` Chengwen Feng
  2022-09-24  7:49   ` [PATCH v3 03/10] memarea: support alloc/free/update-refcnt API Chengwen Feng
                     ` (8 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-24  7:49 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports memarea test about API rte_memarea_create and
rte_memarea_destroy.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 149 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 152 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 4d5bf986c6..eb2de32884 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1562,6 +1562,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index d5cad72116..778de8d65d 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -84,6 +84,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -199,6 +200,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..7c3d78652d
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#ifdef RTE_EXEC_ENV_WINDOWS
+static int
+test_memarea(void)
+{
+	printf("memarea not supported on Windows, skipping test\n");
+	return TEST_SKIPPED;
+}
+
+#else
+
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) \
+			printf("%s Failed\n", #test_func); \
+		else \
+			printf("%s Passed\n", #test_func); \
+	} while (0)
+
+static void
+test_memarea_init_def_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "test-memarea");
+	init->source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int i;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_def_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address align invalid */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	for (i = 1; i < RTE_CACHE_LINE_SIZE; i++) {
+		init.user_addr = (void *)((uintptr_t)i);
+		ma = rte_memarea_create(&init);
+		RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	}
+
+	/* test for user memarea NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for alg invalid */
+	test_memarea_init_def_param(&init);
+	init.alg = RTE_MEMAREA_ALG_DEFAULT + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for create with RTE memory */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_RTE_MEMORY;
+	init.numa_socket = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with system API */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with user-address */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	init.user_addr = (void *)(((uintptr_t)user_buffer + RTE_CACHE_LINE_SIZE) &
+				  ~(RTE_CACHE_LINE_SIZE - 1));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	return 0;
+}
+
+#endif /* !RTE_EXEC_ENV_WINDOWS */
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v3 03/10] memarea: support alloc/free/update-refcnt API
  2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
  2022-09-24  7:49   ` [PATCH v3 01/10] memarea: " Chengwen Feng
  2022-09-24  7:49   ` [PATCH v3 02/10] test/memarea: support memarea test Chengwen Feng
@ 2022-09-24  7:49   ` Chengwen Feng
  2022-09-24  7:49   ` [PATCH v3 04/10] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
                     ` (7 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-24  7:49 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  10 ++
 lib/memarea/memarea_private.h         |   3 +
 lib/memarea/rte_memarea.c             | 143 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  56 ++++++++++
 lib/memarea/version.map               |   3 +
 5 files changed, 215 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index b96dad15f6..41bc0a90cd 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,16 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
+The ``rte_memarea_update_refcnt()`` function is used to update the memory
+object's reference count, if the count reaches zero, the memory object will
+be freed to memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index c76392d3e6..98406879b9 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -25,6 +25,9 @@ struct rte_memarea {
 	void                    *area_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
+
+	uint64_t alloc_fails;
+	uint64_t refcnt_check_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 868da7661d..a072f07f20 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -4,6 +4,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -94,6 +95,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 		ptr = memarea_alloc_from_system_api(init->total_sz);
 	else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
 		ptr = init->user_addr;
+	else if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		ptr = rte_memarea_alloc(init->user_memarea, init->total_sz, 0);
 
 	if (ptr == NULL)
 		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
@@ -145,6 +148,8 @@ memarea_free_area(struct rte_memarea *ma)
 		rte_free(ma->area_addr);
 	else if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
 		free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		rte_memarea_free(ma->init.user_memarea, ma->area_addr);
 }
 
 void
@@ -155,3 +160,141 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(ma);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+#define memarea_roundup(val, align) ((((val) + ((align) - 1)) / (align)) * (align))
+
+static inline bool
+memarea_whether_add_node(size_t free_size, size_t need_size)
+{
+	size_t align_size = memarea_roundup(need_size, RTE_CACHE_LINE_SIZE);
+	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
+}
+
+static inline void
+memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
+{
+	size_t align_size = memarea_roundup(need_size, RTE_CACHE_LINE_SIZE);
+	struct memarea_elem *new_elem;
+	new_elem = (struct memarea_elem *)((uintptr_t)elem + sizeof(struct memarea_elem) +
+					   align_size);
+	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	new_elem->refcnt = 0;
+	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
+	TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
+	elem->size = align_size;
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	struct memarea_elem *elem;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0))
+		return NULL;
+
+	memarea_lock(ma);
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->size < size)
+			continue;
+		if (memarea_whether_add_node(elem->size, size))
+			memarea_add_node(ma, elem, size);
+		elem->cookie = cookie;
+		elem->refcnt = 1;
+		TAILQ_REMOVE(&ma->free_list, elem, free_node);
+		ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));
+		break;
+	}
+	if (unlikely(ptr == NULL))
+		ma->alloc_fails++;
+	memarea_unlock(ma);
+
+	return ptr;
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	rte_memarea_update_refcnt(ma, ptr, -1);
+}
+
+static inline void
+memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
+		   struct memarea_elem *next, bool del_next_from_free,
+		   bool add_curr_to_free)
+{
+	curr->size += next->size + sizeof(struct memarea_elem);
+	next->size = 0;
+	next->cookie = 0;
+	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
+	if (del_next_from_free)
+		TAILQ_REMOVE(&ma->free_list, next, free_node);
+	if (add_curr_to_free) {
+		curr->cookie = MEMAREA_FREE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
+	}
+}
+
+static inline void
+memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
+{
+	struct memarea_elem *prev, *next;
+	bool merged = false;
+	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
+	next = TAILQ_NEXT(elem, elem_node);
+	if (prev != NULL && prev->refcnt == 0) {
+		memarea_merge_node(ma, prev, elem, false, false);
+		elem = prev;
+		merged = true;
+	}
+	if (next != NULL && next->refcnt == 0) {
+		memarea_merge_node(ma, elem, next, true, !merged);
+		merged = true;
+	}
+	if (!merged) {
+		elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	}
+}
+
+void
+rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
+{
+	struct memarea_elem *elem = (struct memarea_elem *)((uintptr_t)ptr -
+							    sizeof(struct memarea_elem));
+
+	if (unlikely(ma == NULL || ptr == NULL))
+		return;
+
+	memarea_lock(ma);
+	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
+		RTE_LOG(ERR, MEMAREA,
+			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
+			ma->init.name, elem->cookie, elem->refcnt, value);
+		ma->refcnt_check_fails++;
+		if (elem->refcnt > 0)
+			elem->refcnt += value;
+		memarea_unlock(ma);
+		return;
+	}
+
+	elem->refcnt += value;
+	if (elem->refcnt == 0)
+		memarea_free_elem(ma, elem);
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 543cda4cac..10e0d6ad5a 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -138,6 +138,62 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ * @param cookie
+ *   User-provided footprint which could used to debug memory leak.
+ *
+ * @return
+ *   Non-NULL on success. Otherwise NULL is returned.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be freed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Update memory's refcnt.
+ *
+ * Update one memory object's refcnt.
+ * When refcnt is updated to be zero, the memory object is freed.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be updated refcnt.
+ * @param value
+ *   The value which need be updated.
+ */
+__rte_experimental
+void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..a0026fc5f9 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,11 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
+	rte_memarea_update_refcnt;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v3 04/10] test/memarea: support alloc/free/update-refcnt test
  2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
                     ` (2 preceding siblings ...)
  2022-09-24  7:49   ` [PATCH v3 03/10] memarea: support alloc/free/update-refcnt API Chengwen Feng
@ 2022-09-24  7:49   ` Chengwen Feng
  2022-09-24  7:49   ` [PATCH v3 05/10] memarea: support dump API Chengwen Feng
                     ` (6 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-24  7:49 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 150 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 149 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 7c3d78652d..0a54ede4c1 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -41,6 +41,12 @@ test_memarea_init_def_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_allocated_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -106,8 +112,8 @@ static int
 test_memarea_create_destroy(void)
 {
 	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea *ma, *user_ma;
 	struct rte_memarea_param init;
-	struct rte_memarea *ma;
 
 	/* test for create with RTE memory */
 	test_memarea_init_def_param(&init);
@@ -133,6 +139,145 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with user-memarea */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	user_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(user_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.user_memarea = user_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(user_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE - RTE_CACHE_LINE_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	ptr[1] = rte_memarea_alloc(ma, 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test invalid parameters with update-refcnt */
+	rte_memarea_update_refcnt(NULL, (void *)(uintptr_t)1, 0);
+	rte_memarea_update_refcnt(ma, NULL, 0);
+	rte_memarea_update_refcnt(NULL, NULL, 0);
+
+	/* test free with refcnt fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	/* test update refcnt with fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_update_refcnt(ma, ptr, -2);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[ALLOC_MAX_NUM];
+	int i;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1, 0);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, ptr[6]);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -141,6 +286,9 @@ test_memarea(void)
 {
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v3 05/10] memarea: support dump API
  2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
                     ` (3 preceding siblings ...)
  2022-09-24  7:49   ` [PATCH v3 04/10] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
@ 2022-09-24  7:49   ` Chengwen Feng
  2022-09-24  7:49   ` [PATCH v3 06/10] test/memarea: support dump test Chengwen Feng
                     ` (5 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-24  7:49 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +
 lib/memarea/rte_memarea.c             | 85 +++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 21 +++++++
 lib/memarea/version.map               |  1 +
 4 files changed, 110 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 41bc0a90cd..c77012fe44 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -43,6 +43,9 @@ The ``rte_memarea_update_refcnt()`` function is used to update the memory
 object's reference count, if the count reaches zero, the memory object will
 be freed to memarea.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index a072f07f20..b70830d0bb 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2022 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/queue.h>
@@ -298,3 +299,87 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		memarea_free_elem(ma, elem);
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
+		return "rte-memory";
+	else if (source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		return "system-api";
+	else if (source == RTE_MEMAREA_SOURCE_USER_ADDR)
+		return "user-addr";
+	else if (source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		return "user-memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_alg alg)
+{
+	if (alg == RTE_MEMAREA_ALG_DEFAULT)
+		return "default";
+	else
+		return "unknown";
+}
+
+static uint32_t
+memarea_elem_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+		num++;
+
+	return num;
+}
+
+static uint32_t
+memarea_free_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->free_list, free_node)
+		num++;
+
+	return num;
+}
+
+static void
+memarea_dump_all(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_elem *elem;
+
+	fprintf(f, "  regions:\n");
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
+			elem->size, elem->cookie, elem->refcnt);
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		fprintf(f, "  source-user-memarea: %s\n", ma->init.user_memarea->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
+	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
+	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
+	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (dump_all)
+		memarea_dump_all(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 10e0d6ad5a..10b8229c64 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -194,6 +194,27 @@ void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 __rte_experimental
 void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index a0026fc5f9..d8ddd93c13 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 	rte_memarea_update_refcnt;
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v3 06/10] test/memarea: support dump test
  2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
                     ` (4 preceding siblings ...)
  2022-09-24  7:49   ` [PATCH v3 05/10] memarea: support dump API Chengwen Feng
@ 2022-09-24  7:49   ` Chengwen Feng
  2022-09-24  7:49   ` [PATCH v3 07/10] memarea: support backup memory mechanism Chengwen Feng
                     ` (4 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-24  7:49 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports rte_memarea_dump() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 0a54ede4c1..ab360f0265 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -274,7 +274,39 @@ test_memarea_alloc_free(void)
 	rte_memarea_free(ma, ptr[5]);
 
 	/* test free NULL */
-	rte_memarea_free(ma, ptr[6]);
+	rte_memarea_free(ma, NULL);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
 
 	rte_memarea_destroy(ma);
 
@@ -289,6 +321,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v3 07/10] memarea: support backup memory mechanism
  2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
                     ` (5 preceding siblings ...)
  2022-09-24  7:49   ` [PATCH v3 06/10] test/memarea: support dump test Chengwen Feng
@ 2022-09-24  7:49   ` Chengwen Feng
  2022-09-24  7:49   ` [PATCH v3 08/10] test/memarea: support backup memory test Chengwen Feng
                     ` (3 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-24  7:49 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports backup memory mechanism, the memarea could use
another memarea as a backup.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +++
 lib/memarea/memarea_private.h         |  2 ++
 lib/memarea/rte_memarea.c             | 22 ++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  7 +++++++
 4 files changed, 34 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index c77012fe44..842d35f77a 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -25,6 +25,9 @@ The main features are as follows:
 
 * It supports MT-safe as long as it's specified at creation time.
 
+* It provides backup memory mechanism, the memarea could use another memarea
+  as a backup.
+
 Library API Overview
 --------------------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 98406879b9..08735ca81f 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -23,11 +23,13 @@ struct rte_memarea {
 	struct rte_memarea_param init;
 	rte_spinlock_t           lock;
 	void                    *area_addr;
+	void                    *top_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
 
 	uint64_t alloc_fails;
 	uint64_t refcnt_check_fails;
+	uint64_t bak_alloc_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index b70830d0bb..f45191aa7f 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -132,6 +132,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
 	TAILQ_INIT(&ma->elem_list);
 	TAILQ_INIT(&ma->free_list);
 	ma->area_addr = addr;
+	ma->top_addr = (void *)((uintptr_t)addr + init->total_sz - 1);
 	elem = addr;
 	elem->size = init->total_sz - sizeof(struct memarea_elem);
 	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
@@ -200,6 +201,15 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
 	elem->size = align_size;
 }
 
+static inline void *
+memarea_alloc_backup(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	void *ptr = rte_memarea_alloc(ma->init.bak_memarea, size, cookie);
+	if (unlikely(ptr == NULL))
+		ma->bak_alloc_fails++;
+	return ptr;
+}
+
 void *
 rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 {
@@ -221,6 +231,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 		ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));
 		break;
 	}
+	if (ptr == NULL && ma->init.bak_memarea != NULL)
+		ptr = memarea_alloc_backup(ma, size, cookie);
 	if (unlikely(ptr == NULL))
 		ma->alloc_fails++;
 	memarea_unlock(ma);
@@ -283,6 +295,12 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		return;
 
 	memarea_lock(ma);
+	if (ptr < ma->area_addr || ptr > ma->top_addr) {
+		rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
+		memarea_unlock(ma);
+		return;
+	}
+
 	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
 		RTE_LOG(ERR, MEMAREA,
 			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
@@ -373,10 +391,14 @@ rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
 	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
 	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
 	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	if (ma->init.bak_memarea)
+		fprintf(f, "  backup-memarea-name: %s\n", ma->init.bak_memarea->init.name);
 	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
 	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
 	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
 	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (ma->init.bak_memarea)
+		fprintf(f, "  backup_alloc_fails: %" PRIu64 "\n", ma->bak_alloc_fails);
 	if (dump_all)
 		memarea_dump_all(ma, f);
 	memarea_unlock(ma);
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 10b8229c64..348febab7f 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -39,6 +39,9 @@
  *   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.
+ * - It provides backup memory mechanism, the memarea could use another memarea
+ *   as a backup. It will attempts to allocate object from backup memarea when
+ *   the current memarea failed to allocate.
  */
 
 #include <stdbool.h>
@@ -105,6 +108,10 @@ struct rte_memarea_param {
 		 */
 		struct rte_memarea *user_memarea;
 	};
+	/** Backup memarea, which is used to handle the scenario where the
+	 * current memarea allocation failure.
+	 */
+	struct rte_memarea *bak_memarea;
 };
 
 /**
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v3 08/10] test/memarea: support backup memory test
  2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
                     ` (6 preceding siblings ...)
  2022-09-24  7:49   ` [PATCH v3 07/10] memarea: support backup memory mechanism Chengwen Feng
@ 2022-09-24  7:49   ` Chengwen Feng
  2022-09-24  7:49   ` [PATCH v3 09/10] memarea: detect memory corruption based on magic Chengwen Feng
                     ` (2 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-24  7:49 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch supports backup memory mechanism test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index ab360f0265..ec3475c354 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -313,6 +313,46 @@ test_memarea_dump(void)
 	return 0;
 }
 
+static int
+test_memarea_backup(void)
+{
+	struct rte_memarea *ma, *bak_ma;
+	struct rte_memarea_param init;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	strcat(init.name, "_backup");
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	bak_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(bak_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE >> 2;
+	init.bak_memarea = bak_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for backup */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 3, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+	rte_memarea_free(ma, ptr);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(bak_ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -322,6 +362,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
+	MEMAREA_TEST_API_RUN(test_memarea_backup);
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v3 09/10] memarea: detect memory corruption based on magic
  2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
                     ` (7 preceding siblings ...)
  2022-09-24  7:49   ` [PATCH v3 08/10] test/memarea: support backup memory test Chengwen Feng
@ 2022-09-24  7:49   ` Chengwen Feng
  2022-09-24  7:49   ` [PATCH v3 10/10] test/memarea: support no MT-safe test Chengwen Feng
  2022-10-03  7:42   ` [PATCH v3 00/10] introduce memarea library David Marchand
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-24  7:49 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

This patch provides lightweight mechanism for detecting memory
corruption which based on magic field in each element node.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 lib/memarea/memarea_private.h |  2 ++
 lib/memarea/rte_memarea.c     | 29 ++++++++++++++++++++++++++---
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 08735ca81f..4f5393e6f9 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -7,10 +7,12 @@
 
 #include <rte_memarea.h>
 
+#define MEMAREA_ELEM_MAGIC_NUM		0xbeef1234
 #define MEMAREA_FREE_ELEM_COOKIE	0xFFFFFFFF
 
 struct memarea_elem {
 	size_t   size;
+	uint32_t magic;
 	uint32_t cookie;
 	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
 	TAILQ_ENTRY(memarea_elem) elem_node;
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index f45191aa7f..c81d4dd3fa 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -135,6 +135,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
 	ma->top_addr = (void *)((uintptr_t)addr + init->total_sz - 1);
 	elem = addr;
 	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->magic = MEMAREA_ELEM_MAGIC_NUM;
 	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
 	elem->refcnt = 0;
 	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
@@ -194,6 +195,7 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
 	new_elem = (struct memarea_elem *)((uintptr_t)elem + sizeof(struct memarea_elem) +
 					   align_size);
 	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->magic = MEMAREA_ELEM_MAGIC_NUM;
 	new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
 	new_elem->refcnt = 0;
 	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
@@ -221,6 +223,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 
 	memarea_lock(ma);
 	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (unlikely(elem->magic != MEMAREA_ELEM_MAGIC_NUM))
+			break;
 		if (elem->size < size)
 			continue;
 		if (memarea_whether_add_node(elem->size, size))
@@ -253,6 +257,7 @@ memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
 {
 	curr->size += next->size + sizeof(struct memarea_elem);
 	next->size = 0;
+	next->magic = ~MEMAREA_ELEM_MAGIC_NUM;
 	next->cookie = 0;
 	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
 	if (del_next_from_free)
@@ -295,6 +300,13 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		return;
 
 	memarea_lock(ma);
+	if (unlikely(elem->magic != MEMAREA_ELEM_MAGIC_NUM)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail!\n",
+			ma->init.name, elem->magic);
+		memarea_unlock(ma);
+		return;
+	}
+
 	if (ptr < ma->area_addr || ptr > ma->top_addr) {
 		rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
 		memarea_unlock(ma);
@@ -348,8 +360,11 @@ memarea_elem_list_num(struct rte_memarea *ma)
 	struct memarea_elem *elem;
 	uint32_t num = 0;
 
-	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM)
+			break;
 		num++;
+	}
 
 	return num;
 }
@@ -360,8 +375,11 @@ memarea_free_list_num(struct rte_memarea *ma)
 	struct memarea_elem *elem;
 	uint32_t num = 0;
 
-	TAILQ_FOREACH(elem, &ma->free_list, free_node)
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM)
+			break;
 		num++;
+	}
 
 	return num;
 }
@@ -372,9 +390,14 @@ memarea_dump_all(struct rte_memarea *ma, FILE *f)
 	struct memarea_elem *elem;
 
 	fprintf(f, "  regions:\n");
-	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM) {
+			fprintf(f, "    magic: 0x%x chech fail!\n", elem->magic);
+			break;
+		}
 		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
 			elem->size, elem->cookie, elem->refcnt);
+	}
 }
 
 int
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v3 10/10] test/memarea: support no MT-safe test
  2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
                     ` (8 preceding siblings ...)
  2022-09-24  7:49   ` [PATCH v3 09/10] memarea: detect memory corruption based on magic Chengwen Feng
@ 2022-09-24  7:49   ` Chengwen Feng
  2022-10-03  7:42   ` [PATCH v3 00/10] introduce memarea library David Marchand
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-09-24  7:49 UTC (permalink / raw)
  To: thomas; +Cc: dev, stephen, jerinjacobk, dmitry.kozliuk

MT-safe is enabled by default in previous test, this patch adds no
MT-safe test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index ec3475c354..b4012086fc 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -353,6 +353,35 @@ test_memarea_backup(void)
 	return 0;
 }
 
+static int
+test_memarea_no_mt_safe(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init.mt_safe = false;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for all API */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	rte_memarea_free(ma, rte_memarea_alloc(ma, 1, 0));
+	rte_memarea_update_refcnt(ma, rte_memarea_alloc(ma, 1, 0), 1);
+	rte_memarea_update_refcnt(ma, rte_memarea_alloc(ma, 1, 0), -1);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -363,6 +392,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
 	MEMAREA_TEST_API_RUN(test_memarea_backup);
+	MEMAREA_TEST_API_RUN(test_memarea_no_mt_safe);
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v3 00/10] introduce memarea library
  2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
                     ` (9 preceding siblings ...)
  2022-09-24  7:49   ` [PATCH v3 10/10] test/memarea: support no MT-safe test Chengwen Feng
@ 2022-10-03  7:42   ` David Marchand
  2022-10-05  4:19     ` datshan
  10 siblings, 1 reply; 222+ messages in thread
From: David Marchand @ 2022-10-03  7:42 UTC (permalink / raw)
  To: Chengwen Feng, Morten Brørup, Burakov, Anatoly,
	dmitry.kozliuk, stephen
  Cc: thomas, dev, jerinjacobk

On Sat, Sep 24, 2022 at 9:56 AM Chengwen Feng <fengchengwen@huawei.com> wrote:
>
> The memarea library is an allocator of variable-size object which based
> on a memory region. The main features are as follows:
>
> - The default alignment size is RTE_CACHE_LINE_SIZE.
>
> - The memory region can be initialized from the following memory
>   sources:
>   1. RTE memory: e.g. invoke rte_malloc_socket to obtain.
>   2. System API: e.g. invoke posix_memalign to obtain.
>   3. User provided address: it can be from extended memory as long as
>      it is available. The address must be aligned to
>      RTE_CACHE_LINE_SIZE.
>   4. User provided memarea: it can be from another memarea.
>
> - It provides refcnt feature which could be useful in multi-reader
>   scenario.
>
> - It provides backup memory mechanism, the memarea could use another
>   memarea as a backup.
>
> Note:
> a) The memarea is oriented towards the application layer, which could
> provides 'region-based memory management' [1] function.
> b) The eal library also provide memory zone/heap management, but these
> are tied to huge pages management.
>
> [1] https://en.wikipedia.org/wiki/Region-based_memory_management
>
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>

This series did not pass through the CI, as its patches are spread
over different series in patchwork.
https://patchwork.dpdk.org/project/dpdk/list/?submitter=2146

There was probably something wrong when sending the patches, please
check your setup and repost them.


Thanks.

-- 
David Marchand


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v4 00/10] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (5 preceding siblings ...)
  2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
@ 2022-10-05  3:38 ` datshan
       [not found] ` <20221005033848.2241-1-datshan@qq.com>
                   ` (16 subsequent siblings)
  23 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  3:38 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, datshan, Chengwen Feng

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- The default alignment size is RTE_CACHE_LINE_SIZE.

- The memory region can be initialized from the following memory
  sources:
  1. RTE memory: e.g. invoke rte_malloc_socket to obtain.
  2. System API: e.g. invoke posix_memalign to obtain.
  3. User provided address: it can be from extended memory as long as
     it is available. The address must be aligned to
     RTE_CACHE_LINE_SIZE.
  4. User provided memarea: it can be from another memarea.

- It provides refcnt feature which could be useful in multi-reader
  scenario.

- It provides backup memory mechanism, the memarea could use another
  memarea as a backup.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>

Chengwen Feng (10):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc/free/update-refcnt API
  test/memarea: support alloc/free/update-refcnt test
  memarea: support dump API
  test/memarea: support dump test
  memarea: support backup memory mechanism
  test/memarea: support backup memory test
  memarea: detect memory corruption based on magic
  test/memarea: support no MT-safe test

---
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 401 +++++++++++++++++++++++
 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  |  55 ++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  37 +++
 lib/memarea/meson.build                |  16 +
 lib/memarea/rte_memarea.c              | 430 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 229 +++++++++++++
 lib/memarea/version.map                |  16 +
 lib/meson.build                        |   1 +
 16 files changed, 1205 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v4 01/10] memarea: introduce memarea library
       [not found] ` <20221005033848.2241-1-datshan@qq.com>
@ 2022-10-05  3:38   ` datshan
  2022-10-05  3:38   ` [PATCH v4 02/10] test/memarea: support memarea test datshan
                     ` (8 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  3:38 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

The memarea library is an allocator of variable-size object which based
on a memory region.

This patch provides create/destroy API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.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  |  39 ++++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  30 +++++
 lib/memarea/meson.build                |  16 +++
 lib/memarea/rte_memarea.c              | 157 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 145 +++++++++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 14 files changed, 417 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 a55b379d73..b9c638221d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1550,6 +1550,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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index de488c7abf..24456604f8 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -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),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index f0886c3bd1..8334ebcbd6 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -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 \
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..b96dad15f6
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -0,0 +1,39 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2022 HiSilicon Limited
+
+Memarea Library
+===============
+
+Introduction
+------------
+
+The memarea library provides an allocator of variable-size objects, it is
+oriented towards the application layer, which could provides 'region-based
+memory management' function [1].
+
+The main features are as follows:
+
+* The default aligement size is ``RTE_CACHE_LINE_SIZE``.
+
+* The memory region can be initialized from the following memory sources:
+  a) RTE memory: e.g. invoke ``rte_malloc_socket`` to obtain. b) System API:
+  e.g. invoke posix_memalign to obtain. c) User provided address: it can be from
+  extendedd memory as long as it is available. d) User provided memarea: it can
+  be from another memarea.
+
+* It provides refcnt feature which could be useful in multi-reader scenario.
+
+* 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.
+
+Reference
+---------
+
+[1] https://en.wikipedia.org/wiki/Region-based_memory_management
diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
index 5d8ef669b8..4c1f760b98 100644
--- a/doc/guides/rel_notes/release_22_11.rst
+++ b/doc/guides/rel_notes/release_22_11.rst
@@ -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, which could provides 'region-based memory
+  management' function.
+
 * **Added configuration for asynchronous flow connection tracking.**
 
   Added connection tracking action number hint to ``rte_flow_configure``
diff --git a/lib/eal/common/eal_common_log.c b/lib/eal/common/eal_common_log.c
index bd7b188ceb..3d62af59c6 100644
--- a/lib/eal/common/eal_common_log.c
+++ b/lib/eal/common/eal_common_log.c
@@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
 	{RTE_LOGTYPE_EFD,        "lib.efd"},
 	{RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
 	{RTE_LOGTYPE_GSO,        "lib.gso"},
+	{RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
 	{RTE_LOGTYPE_USER1,      "user1"},
 	{RTE_LOGTYPE_USER2,      "user2"},
 	{RTE_LOGTYPE_USER3,      "user3"},
diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
index 25ce42cdfc..708f3a39dd 100644
--- a/lib/eal/include/rte_log.h
+++ b/lib/eal/include/rte_log.h
@@ -48,6 +48,7 @@ extern "C" {
 #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
 #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
 #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
+#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..c76392d3e6
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef MEMAREA_PRIVATE_H
+#define MEMAREA_PRIVATE_H
+
+#include <rte_memarea.h>
+
+#define MEMAREA_FREE_ELEM_COOKIE	0xFFFFFFFF
+
+struct memarea_elem {
+	size_t   size;
+	uint32_t cookie;
+	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
+	TAILQ_ENTRY(memarea_elem) elem_node;
+	TAILQ_ENTRY(memarea_elem) free_node;
+} __rte_cache_aligned;
+
+TAILQ_HEAD(memarea_elem_list, memarea_elem);
+
+struct rte_memarea {
+	struct rte_memarea_param init;
+	rte_spinlock_t           lock;
+	void                    *area_addr;
+	struct memarea_elem_list elem_list;
+	struct memarea_elem_list free_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..0a74fb4cd1
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2022 HiSilicon Limited
+
+if is_windows
+    build = false
+    reason = 'not supported on Windows'
+    subdir_done()
+endif
+
+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..868da7661d
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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"
+
+static int
+memarea_check_param(const struct rte_memarea_param *init)
+{
+	size_t len;
+
+	if (init == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
+		return -EINVAL;
+	}
+
+	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
+	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
+		return -EINVAL;
+	}
+
+	if (init->source != RTE_MEMAREA_SOURCE_RTE_MEMORY &&
+	    init->source != RTE_MEMAREA_SOURCE_SYSTEM_API &&
+	    init->source != RTE_MEMAREA_SOURCE_USER_ADDR &&
+	    init->source != RTE_MEMAREA_SOURCE_USER_MEMAREA) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
+			init->name, init->source);
+		return -EINVAL;
+	}
+
+	if (init->total_sz <= sizeof(struct memarea_elem)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
+			init->name, init->total_sz);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR && init->user_addr == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR &&
+	    ((uintptr_t)init->user_addr & (RTE_CACHE_LINE_SIZE - 1))) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr should align: %d!\n",
+			init->name, RTE_CACHE_LINE_SIZE);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA && init->user_memarea == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided memarea is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->alg != RTE_MEMAREA_ALG_DEFAULT) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
+			init->name, init->alg);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+memarea_alloc_from_system_api(size_t size)
+{
+	void *ptr = NULL;
+	int ret;
+
+	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+	if (ret)
+		return NULL;
+	return ptr;
+}
+
+static void *
+memarea_alloc_area(const struct rte_memarea_param *init)
+{
+	void *ptr = NULL;
+
+	if (init->source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
+		ptr = rte_malloc_socket(NULL, init->total_sz, RTE_CACHE_LINE_SIZE,
+					init->numa_socket);
+	else if (init->source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		ptr = memarea_alloc_from_system_api(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
+		ptr = init->user_addr;
+
+	if (ptr == NULL)
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
+
+	return ptr;
+}
+
+struct rte_memarea *
+rte_memarea_create(const struct rte_memarea_param *init)
+{
+	struct memarea_elem *elem;
+	struct rte_memarea *ma;
+	void *addr;
+	int ret;
+
+	ret = memarea_check_param(init);
+	if (ret)
+		return NULL;
+
+	addr = memarea_alloc_area(init);
+	if (addr == NULL)
+		return NULL;
+
+	ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
+	if (ma == NULL) {
+		RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
+		return NULL;
+	}
+
+	ma->init = *init;
+	rte_spinlock_init(&ma->lock);
+	TAILQ_INIT(&ma->elem_list);
+	TAILQ_INIT(&ma->free_list);
+	ma->area_addr = addr;
+	elem = addr;
+	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	elem->refcnt = 0;
+	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
+	TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+
+	return ma;
+}
+
+static void
+memarea_free_area(struct rte_memarea *ma)
+{
+	if (ma->init.source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
+		rte_free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		free(ma->area_addr);
+}
+
+void
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+	if (ma == NULL)
+		return;
+	memarea_free_area(ma);
+	rte_free(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
new file mode 100644
index 0000000000..543cda4cac
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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 default alignment size is RTE_CACHE_LINE_SIZE.
+ * - The memory region can be initialized from the following memory sources:
+ *   1. RTE memory: e.g. invoke rte_malloc_socket to obtain.
+ *   2. System API: e.g. invoke posix_memalign to obtain.
+ *   3. User provided address: it can be from extended memory as long as it is
+ *      available. The address must be aligned to RTE_CACHE_LINE_SIZE.
+ *   4) User provided memarea: it can be from another memarea. So we can build
+ *      the following memory management structure:
+ *         \code{.unparsed}
+ *                           -------------
+ *                           | memarea-1 |
+ *                           -------------
+ *                                 |
+ *                                 v
+ *                  -------------------------------
+ *                  |               |             |
+ *                  v               v             v
+ *            -------------   -------------   ----------
+ *            | memarea-2 |   | memarea-3 |   | object |
+ *            -------------   -------------   ----------
+ *         \endcode
+ *      As shown above, the memarea-2/3 both create from memarea-1's memory.
+ * - It provides refcnt feature which could be useful in multi-reader scenario.
+ * - 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_RTE_MEMORY,
+	/** Memory source comes from system API. */
+	RTE_MEMAREA_SOURCE_SYSTEM_API,
+	/** Memory source comes from user-provided address. */
+	RTE_MEMAREA_SOURCE_USER_ADDR,
+	/** Memory source comes from user-provided memarea. */
+	RTE_MEMAREA_SOURCE_USER_MEMAREA,
+};
+
+/**
+ * Memarea memory management algorithm.
+ */
+enum rte_memarea_alg {
+	/* The default management algorithm is a variant of the next fit
+	 * algorithm. It uses a free-list to apply for memory and uses an
+	 * element-list in ascending order of address to support merging
+	 * upon free.
+	 */
+	RTE_MEMAREA_ALG_DEFAULT,
+};
+
+struct rte_memarea;
+
+struct rte_memarea_param {
+	char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
+	enum rte_memarea_source source;  /**< Memory source of memarea. */
+	enum rte_memarea_alg 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_RTE_MEMORY.
+		 */
+		int numa_socket;
+		/** User provided address, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER_ADDR.
+		 * Note: the provided address must align at least
+		 * RTE_CACHE_LINE_SIZE.
+		 */
+		void *user_addr;
+		/** User provided memarea, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER_MEMAREA.
+		 */
+		struct rte_memarea *user_memarea;
+	};
+};
+
+/**
+ * @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 */
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 c648f7d800..521a25d6c0 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v4 02/10] test/memarea: support memarea test
       [not found] ` <20221005033848.2241-1-datshan@qq.com>
  2022-10-05  3:38   ` [PATCH v4 01/10] memarea: " datshan
@ 2022-10-05  3:38   ` datshan
  2022-10-05  3:38   ` [PATCH v4 03/10] memarea: support alloc/free/update-refcnt API datshan
                     ` (7 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  3:38 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports memarea test about API rte_memarea_create and
rte_memarea_destroy.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 149 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 152 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index b9c638221d..bfdb36f1b5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1554,6 +1554,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index d5cad72116..778de8d65d 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -84,6 +84,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -199,6 +200,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..7c3d78652d
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#ifdef RTE_EXEC_ENV_WINDOWS
+static int
+test_memarea(void)
+{
+	printf("memarea not supported on Windows, skipping test\n");
+	return TEST_SKIPPED;
+}
+
+#else
+
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) \
+			printf("%s Failed\n", #test_func); \
+		else \
+			printf("%s Passed\n", #test_func); \
+	} while (0)
+
+static void
+test_memarea_init_def_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "test-memarea");
+	init->source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int i;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_def_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address align invalid */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	for (i = 1; i < RTE_CACHE_LINE_SIZE; i++) {
+		init.user_addr = (void *)((uintptr_t)i);
+		ma = rte_memarea_create(&init);
+		RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	}
+
+	/* test for user memarea NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for alg invalid */
+	test_memarea_init_def_param(&init);
+	init.alg = RTE_MEMAREA_ALG_DEFAULT + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for create with RTE memory */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_RTE_MEMORY;
+	init.numa_socket = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with system API */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with user-address */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	init.user_addr = (void *)(((uintptr_t)user_buffer + RTE_CACHE_LINE_SIZE) &
+				  ~(RTE_CACHE_LINE_SIZE - 1));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	return 0;
+}
+
+#endif /* !RTE_EXEC_ENV_WINDOWS */
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v4 03/10] memarea: support alloc/free/update-refcnt API
       [not found] ` <20221005033848.2241-1-datshan@qq.com>
  2022-10-05  3:38   ` [PATCH v4 01/10] memarea: " datshan
  2022-10-05  3:38   ` [PATCH v4 02/10] test/memarea: support memarea test datshan
@ 2022-10-05  3:38   ` datshan
  2022-10-05  3:38   ` [PATCH v4 04/10] test/memarea: support alloc/free/update-refcnt test datshan
                     ` (6 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  3:38 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  10 ++
 lib/memarea/memarea_private.h         |   3 +
 lib/memarea/rte_memarea.c             | 143 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  56 ++++++++++
 lib/memarea/version.map               |   3 +
 5 files changed, 215 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index b96dad15f6..41bc0a90cd 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,16 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
+The ``rte_memarea_update_refcnt()`` function is used to update the memory
+object's reference count, if the count reaches zero, the memory object will
+be freed to memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index c76392d3e6..98406879b9 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -25,6 +25,9 @@ struct rte_memarea {
 	void                    *area_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
+
+	uint64_t alloc_fails;
+	uint64_t refcnt_check_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 868da7661d..a072f07f20 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -4,6 +4,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -94,6 +95,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 		ptr = memarea_alloc_from_system_api(init->total_sz);
 	else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
 		ptr = init->user_addr;
+	else if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		ptr = rte_memarea_alloc(init->user_memarea, init->total_sz, 0);
 
 	if (ptr == NULL)
 		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
@@ -145,6 +148,8 @@ memarea_free_area(struct rte_memarea *ma)
 		rte_free(ma->area_addr);
 	else if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
 		free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		rte_memarea_free(ma->init.user_memarea, ma->area_addr);
 }
 
 void
@@ -155,3 +160,141 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(ma);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+#define memarea_roundup(val, align) ((((val) + ((align) - 1)) / (align)) * (align))
+
+static inline bool
+memarea_whether_add_node(size_t free_size, size_t need_size)
+{
+	size_t align_size = memarea_roundup(need_size, RTE_CACHE_LINE_SIZE);
+	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
+}
+
+static inline void
+memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
+{
+	size_t align_size = memarea_roundup(need_size, RTE_CACHE_LINE_SIZE);
+	struct memarea_elem *new_elem;
+	new_elem = (struct memarea_elem *)((uintptr_t)elem + sizeof(struct memarea_elem) +
+					   align_size);
+	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	new_elem->refcnt = 0;
+	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
+	TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
+	elem->size = align_size;
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	struct memarea_elem *elem;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0))
+		return NULL;
+
+	memarea_lock(ma);
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->size < size)
+			continue;
+		if (memarea_whether_add_node(elem->size, size))
+			memarea_add_node(ma, elem, size);
+		elem->cookie = cookie;
+		elem->refcnt = 1;
+		TAILQ_REMOVE(&ma->free_list, elem, free_node);
+		ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));
+		break;
+	}
+	if (unlikely(ptr == NULL))
+		ma->alloc_fails++;
+	memarea_unlock(ma);
+
+	return ptr;
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	rte_memarea_update_refcnt(ma, ptr, -1);
+}
+
+static inline void
+memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
+		   struct memarea_elem *next, bool del_next_from_free,
+		   bool add_curr_to_free)
+{
+	curr->size += next->size + sizeof(struct memarea_elem);
+	next->size = 0;
+	next->cookie = 0;
+	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
+	if (del_next_from_free)
+		TAILQ_REMOVE(&ma->free_list, next, free_node);
+	if (add_curr_to_free) {
+		curr->cookie = MEMAREA_FREE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
+	}
+}
+
+static inline void
+memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
+{
+	struct memarea_elem *prev, *next;
+	bool merged = false;
+	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
+	next = TAILQ_NEXT(elem, elem_node);
+	if (prev != NULL && prev->refcnt == 0) {
+		memarea_merge_node(ma, prev, elem, false, false);
+		elem = prev;
+		merged = true;
+	}
+	if (next != NULL && next->refcnt == 0) {
+		memarea_merge_node(ma, elem, next, true, !merged);
+		merged = true;
+	}
+	if (!merged) {
+		elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	}
+}
+
+void
+rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
+{
+	struct memarea_elem *elem = (struct memarea_elem *)((uintptr_t)ptr -
+							    sizeof(struct memarea_elem));
+
+	if (unlikely(ma == NULL || ptr == NULL))
+		return;
+
+	memarea_lock(ma);
+	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
+		RTE_LOG(ERR, MEMAREA,
+			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
+			ma->init.name, elem->cookie, elem->refcnt, value);
+		ma->refcnt_check_fails++;
+		if (elem->refcnt > 0)
+			elem->refcnt += value;
+		memarea_unlock(ma);
+		return;
+	}
+
+	elem->refcnt += value;
+	if (elem->refcnt == 0)
+		memarea_free_elem(ma, elem);
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 543cda4cac..10e0d6ad5a 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -138,6 +138,62 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ * @param cookie
+ *   User-provided footprint which could used to debug memory leak.
+ *
+ * @return
+ *   Non-NULL on success. Otherwise NULL is returned.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be freed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Update memory's refcnt.
+ *
+ * Update one memory object's refcnt.
+ * When refcnt is updated to be zero, the memory object is freed.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be updated refcnt.
+ * @param value
+ *   The value which need be updated.
+ */
+__rte_experimental
+void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..a0026fc5f9 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,11 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
+	rte_memarea_update_refcnt;
 
 	local: *;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v4 04/10] test/memarea: support alloc/free/update-refcnt test
       [not found] ` <20221005033848.2241-1-datshan@qq.com>
                     ` (2 preceding siblings ...)
  2022-10-05  3:38   ` [PATCH v4 03/10] memarea: support alloc/free/update-refcnt API datshan
@ 2022-10-05  3:38   ` datshan
  2022-10-05  3:38   ` [PATCH v4 05/10] memarea: support dump API datshan
                     ` (5 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  3:38 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 150 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 149 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 7c3d78652d..0a54ede4c1 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -41,6 +41,12 @@ test_memarea_init_def_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_allocated_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -106,8 +112,8 @@ static int
 test_memarea_create_destroy(void)
 {
 	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea *ma, *user_ma;
 	struct rte_memarea_param init;
-	struct rte_memarea *ma;
 
 	/* test for create with RTE memory */
 	test_memarea_init_def_param(&init);
@@ -133,6 +139,145 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with user-memarea */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	user_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(user_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.user_memarea = user_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(user_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE - RTE_CACHE_LINE_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	ptr[1] = rte_memarea_alloc(ma, 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test invalid parameters with update-refcnt */
+	rte_memarea_update_refcnt(NULL, (void *)(uintptr_t)1, 0);
+	rte_memarea_update_refcnt(ma, NULL, 0);
+	rte_memarea_update_refcnt(NULL, NULL, 0);
+
+	/* test free with refcnt fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	/* test update refcnt with fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_update_refcnt(ma, ptr, -2);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[ALLOC_MAX_NUM];
+	int i;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1, 0);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, ptr[6]);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -141,6 +286,9 @@ test_memarea(void)
 {
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	return 0;
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v4 05/10] memarea: support dump API
       [not found] ` <20221005033848.2241-1-datshan@qq.com>
                     ` (3 preceding siblings ...)
  2022-10-05  3:38   ` [PATCH v4 04/10] test/memarea: support alloc/free/update-refcnt test datshan
@ 2022-10-05  3:38   ` datshan
  2022-10-05  3:38   ` [PATCH v4 06/10] test/memarea: support dump test datshan
                     ` (4 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  3:38 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +
 lib/memarea/rte_memarea.c             | 85 +++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 21 +++++++
 lib/memarea/version.map               |  1 +
 4 files changed, 110 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 41bc0a90cd..c77012fe44 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -43,6 +43,9 @@ The ``rte_memarea_update_refcnt()`` function is used to update the memory
 object's reference count, if the count reaches zero, the memory object will
 be freed to memarea.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index a072f07f20..b70830d0bb 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2022 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/queue.h>
@@ -298,3 +299,87 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		memarea_free_elem(ma, elem);
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
+		return "rte-memory";
+	else if (source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		return "system-api";
+	else if (source == RTE_MEMAREA_SOURCE_USER_ADDR)
+		return "user-addr";
+	else if (source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		return "user-memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_alg alg)
+{
+	if (alg == RTE_MEMAREA_ALG_DEFAULT)
+		return "default";
+	else
+		return "unknown";
+}
+
+static uint32_t
+memarea_elem_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+		num++;
+
+	return num;
+}
+
+static uint32_t
+memarea_free_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->free_list, free_node)
+		num++;
+
+	return num;
+}
+
+static void
+memarea_dump_all(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_elem *elem;
+
+	fprintf(f, "  regions:\n");
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
+			elem->size, elem->cookie, elem->refcnt);
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		fprintf(f, "  source-user-memarea: %s\n", ma->init.user_memarea->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
+	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
+	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
+	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (dump_all)
+		memarea_dump_all(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 10e0d6ad5a..10b8229c64 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -194,6 +194,27 @@ void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 __rte_experimental
 void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index a0026fc5f9..d8ddd93c13 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 	rte_memarea_update_refcnt;
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v4 06/10] test/memarea: support dump test
       [not found] ` <20221005033848.2241-1-datshan@qq.com>
                     ` (4 preceding siblings ...)
  2022-10-05  3:38   ` [PATCH v4 05/10] memarea: support dump API datshan
@ 2022-10-05  3:38   ` datshan
  2022-10-05  3:38   ` [PATCH v4 07/10] memarea: support backup memory mechanism datshan
                     ` (3 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  3:38 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports rte_memarea_dump() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 0a54ede4c1..ab360f0265 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -274,7 +274,39 @@ test_memarea_alloc_free(void)
 	rte_memarea_free(ma, ptr[5]);
 
 	/* test free NULL */
-	rte_memarea_free(ma, ptr[6]);
+	rte_memarea_free(ma, NULL);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
 
 	rte_memarea_destroy(ma);
 
@@ -289,6 +321,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 	return 0;
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v4 07/10] memarea: support backup memory mechanism
       [not found] ` <20221005033848.2241-1-datshan@qq.com>
                     ` (5 preceding siblings ...)
  2022-10-05  3:38   ` [PATCH v4 06/10] test/memarea: support dump test datshan
@ 2022-10-05  3:38   ` datshan
  2022-10-05  3:38   ` [PATCH v4 08/10] test/memarea: support backup memory test datshan
                     ` (2 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  3:38 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports backup memory mechanism, the memarea could use
another memarea as a backup.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +++
 lib/memarea/memarea_private.h         |  2 ++
 lib/memarea/rte_memarea.c             | 22 ++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  7 +++++++
 4 files changed, 34 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index c77012fe44..842d35f77a 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -25,6 +25,9 @@ The main features are as follows:
 
 * It supports MT-safe as long as it's specified at creation time.
 
+* It provides backup memory mechanism, the memarea could use another memarea
+  as a backup.
+
 Library API Overview
 --------------------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 98406879b9..08735ca81f 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -23,11 +23,13 @@ struct rte_memarea {
 	struct rte_memarea_param init;
 	rte_spinlock_t           lock;
 	void                    *area_addr;
+	void                    *top_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
 
 	uint64_t alloc_fails;
 	uint64_t refcnt_check_fails;
+	uint64_t bak_alloc_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index b70830d0bb..f45191aa7f 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -132,6 +132,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
 	TAILQ_INIT(&ma->elem_list);
 	TAILQ_INIT(&ma->free_list);
 	ma->area_addr = addr;
+	ma->top_addr = (void *)((uintptr_t)addr + init->total_sz - 1);
 	elem = addr;
 	elem->size = init->total_sz - sizeof(struct memarea_elem);
 	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
@@ -200,6 +201,15 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
 	elem->size = align_size;
 }
 
+static inline void *
+memarea_alloc_backup(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	void *ptr = rte_memarea_alloc(ma->init.bak_memarea, size, cookie);
+	if (unlikely(ptr == NULL))
+		ma->bak_alloc_fails++;
+	return ptr;
+}
+
 void *
 rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 {
@@ -221,6 +231,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 		ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));
 		break;
 	}
+	if (ptr == NULL && ma->init.bak_memarea != NULL)
+		ptr = memarea_alloc_backup(ma, size, cookie);
 	if (unlikely(ptr == NULL))
 		ma->alloc_fails++;
 	memarea_unlock(ma);
@@ -283,6 +295,12 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		return;
 
 	memarea_lock(ma);
+	if (ptr < ma->area_addr || ptr > ma->top_addr) {
+		rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
+		memarea_unlock(ma);
+		return;
+	}
+
 	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
 		RTE_LOG(ERR, MEMAREA,
 			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
@@ -373,10 +391,14 @@ rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
 	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
 	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
 	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	if (ma->init.bak_memarea)
+		fprintf(f, "  backup-memarea-name: %s\n", ma->init.bak_memarea->init.name);
 	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
 	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
 	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
 	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (ma->init.bak_memarea)
+		fprintf(f, "  backup_alloc_fails: %" PRIu64 "\n", ma->bak_alloc_fails);
 	if (dump_all)
 		memarea_dump_all(ma, f);
 	memarea_unlock(ma);
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 10b8229c64..348febab7f 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -39,6 +39,9 @@
  *   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.
+ * - It provides backup memory mechanism, the memarea could use another memarea
+ *   as a backup. It will attempts to allocate object from backup memarea when
+ *   the current memarea failed to allocate.
  */
 
 #include <stdbool.h>
@@ -105,6 +108,10 @@ struct rte_memarea_param {
 		 */
 		struct rte_memarea *user_memarea;
 	};
+	/** Backup memarea, which is used to handle the scenario where the
+	 * current memarea allocation failure.
+	 */
+	struct rte_memarea *bak_memarea;
 };
 
 /**
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v4 08/10] test/memarea: support backup memory test
       [not found] ` <20221005033848.2241-1-datshan@qq.com>
                     ` (6 preceding siblings ...)
  2022-10-05  3:38   ` [PATCH v4 07/10] memarea: support backup memory mechanism datshan
@ 2022-10-05  3:38   ` datshan
  2022-10-05  3:38   ` [PATCH v4 09/10] memarea: detect memory corruption based on magic datshan
  2022-10-05  3:38   ` [PATCH v4 10/10] test/memarea: support no MT-safe test datshan
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  3:38 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports backup memory mechanism test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index ab360f0265..ec3475c354 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -313,6 +313,46 @@ test_memarea_dump(void)
 	return 0;
 }
 
+static int
+test_memarea_backup(void)
+{
+	struct rte_memarea *ma, *bak_ma;
+	struct rte_memarea_param init;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	strcat(init.name, "_backup");
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	bak_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(bak_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE >> 2;
+	init.bak_memarea = bak_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for backup */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 3, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+	rte_memarea_free(ma, ptr);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(bak_ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -322,6 +362,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
+	MEMAREA_TEST_API_RUN(test_memarea_backup);
 	return 0;
 }
 
-- 
2.34.1



^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v4 09/10] memarea: detect memory corruption based on magic
       [not found] ` <20221005033848.2241-1-datshan@qq.com>
                     ` (7 preceding siblings ...)
  2022-10-05  3:38   ` [PATCH v4 08/10] test/memarea: support backup memory test datshan
@ 2022-10-05  3:38   ` datshan
  2022-10-05  3:38   ` [PATCH v4 10/10] test/memarea: support no MT-safe test datshan
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  3:38 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch provides lightweight mechanism for detecting memory
corruption which based on magic field in each element node.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 lib/memarea/memarea_private.h |  2 ++
 lib/memarea/rte_memarea.c     | 29 ++++++++++++++++++++++++++---
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 08735ca81f..4f5393e6f9 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -7,10 +7,12 @@
 
 #include <rte_memarea.h>
 
+#define MEMAREA_ELEM_MAGIC_NUM		0xbeef1234
 #define MEMAREA_FREE_ELEM_COOKIE	0xFFFFFFFF
 
 struct memarea_elem {
 	size_t   size;
+	uint32_t magic;
 	uint32_t cookie;
 	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
 	TAILQ_ENTRY(memarea_elem) elem_node;
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index f45191aa7f..c81d4dd3fa 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -135,6 +135,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
 	ma->top_addr = (void *)((uintptr_t)addr + init->total_sz - 1);
 	elem = addr;
 	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->magic = MEMAREA_ELEM_MAGIC_NUM;
 	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
 	elem->refcnt = 0;
 	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
@@ -194,6 +195,7 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
 	new_elem = (struct memarea_elem *)((uintptr_t)elem + sizeof(struct memarea_elem) +
 					   align_size);
 	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->magic = MEMAREA_ELEM_MAGIC_NUM;
 	new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
 	new_elem->refcnt = 0;
 	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
@@ -221,6 +223,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 
 	memarea_lock(ma);
 	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (unlikely(elem->magic != MEMAREA_ELEM_MAGIC_NUM))
+			break;
 		if (elem->size < size)
 			continue;
 		if (memarea_whether_add_node(elem->size, size))
@@ -253,6 +257,7 @@ memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
 {
 	curr->size += next->size + sizeof(struct memarea_elem);
 	next->size = 0;
+	next->magic = ~MEMAREA_ELEM_MAGIC_NUM;
 	next->cookie = 0;
 	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
 	if (del_next_from_free)
@@ -295,6 +300,13 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		return;
 
 	memarea_lock(ma);
+	if (unlikely(elem->magic != MEMAREA_ELEM_MAGIC_NUM)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail!\n",
+			ma->init.name, elem->magic);
+		memarea_unlock(ma);
+		return;
+	}
+
 	if (ptr < ma->area_addr || ptr > ma->top_addr) {
 		rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
 		memarea_unlock(ma);
@@ -348,8 +360,11 @@ memarea_elem_list_num(struct rte_memarea *ma)
 	struct memarea_elem *elem;
 	uint32_t num = 0;
 
-	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM)
+			break;
 		num++;
+	}
 
 	return num;
 }
@@ -360,8 +375,11 @@ memarea_free_list_num(struct rte_memarea *ma)
 	struct memarea_elem *elem;
 	uint32_t num = 0;
 
-	TAILQ_FOREACH(elem, &ma->free_list, free_node)
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM)
+			break;
 		num++;
+	}
 
 	return num;
 }
@@ -372,9 +390,14 @@ memarea_dump_all(struct rte_memarea *ma, FILE *f)
 	struct memarea_elem *elem;
 
 	fprintf(f, "  regions:\n");
-	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM) {
+			fprintf(f, "    magic: 0x%x chech fail!\n", elem->magic);
+			break;
+		}
 		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
 			elem->size, elem->cookie, elem->refcnt);
+	}
 }
 
 int
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v4 10/10] test/memarea: support no MT-safe test
       [not found] ` <20221005033848.2241-1-datshan@qq.com>
                     ` (8 preceding siblings ...)
  2022-10-05  3:38   ` [PATCH v4 09/10] memarea: detect memory corruption based on magic datshan
@ 2022-10-05  3:38   ` datshan
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  3:38 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

MT-safe is enabled by default in previous test, this patch adds no
MT-safe test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index ec3475c354..b4012086fc 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -353,6 +353,35 @@ test_memarea_backup(void)
 	return 0;
 }
 
+static int
+test_memarea_no_mt_safe(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init.mt_safe = false;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for all API */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	rte_memarea_free(ma, rte_memarea_alloc(ma, 1, 0));
+	rte_memarea_update_refcnt(ma, rte_memarea_alloc(ma, 1, 0), 1);
+	rte_memarea_update_refcnt(ma, rte_memarea_alloc(ma, 1, 0), -1);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -363,6 +392,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
 	MEMAREA_TEST_API_RUN(test_memarea_backup);
+	MEMAREA_TEST_API_RUN(test_memarea_no_mt_safe);
 	return 0;
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v5 00/10] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (7 preceding siblings ...)
       [not found] ` <20221005033848.2241-1-datshan@qq.com>
@ 2022-10-05  4:09 ` datshan
       [not found] ` <20221005040952.8166-1-datshan@qq.com>
                   ` (14 subsequent siblings)
  23 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  4:09 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, datshan, Chengwen Feng

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- The default alignment size is RTE_CACHE_LINE_SIZE.

- The memory region can be initialized from the following memory
  sources:
  1. RTE memory: e.g. invoke rte_malloc_socket to obtain.
  2. System API: e.g. invoke posix_memalign to obtain.
  3. User provided address: it can be from extended memory as long as
     it is available. The address must be aligned to
     RTE_CACHE_LINE_SIZE.
  4. User provided memarea: it can be from another memarea.

- It provides refcnt feature which could be useful in multi-reader
  scenario.

- It provides backup memory mechanism, the memarea could use another
  memarea as a backup.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>

Chengwen Feng (10):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc/free/update-refcnt API
  test/memarea: support alloc/free/update-refcnt test
  memarea: support dump API
  test/memarea: support dump test
  memarea: support backup memory mechanism
  test/memarea: support backup memory test
  memarea: detect memory corruption based on magic
  test/memarea: support no MT-safe test

---
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 401 +++++++++++++++++++++++
 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  |  55 ++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  37 +++
 lib/memarea/meson.build                |  16 +
 lib/memarea/rte_memarea.c              | 430 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 229 +++++++++++++
 lib/memarea/version.map                |  16 +
 lib/meson.build                        |   1 +
 16 files changed, 1205 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v5 01/10] memarea: introduce memarea library
       [not found] ` <20221005040952.8166-1-datshan@qq.com>
@ 2022-10-05  4:09   ` datshan
  2022-10-06 20:15     ` Mattias Rönnblom
  2022-10-05  4:09   ` [PATCH v5 02/10] test/memarea: support memarea test datshan
                     ` (8 subsequent siblings)
  9 siblings, 1 reply; 222+ messages in thread
From: datshan @ 2022-10-05  4:09 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

The memarea library is an allocator of variable-size object which based
on a memory region.

This patch provides create/destroy API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.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  |  39 ++++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  30 +++++
 lib/memarea/meson.build                |  16 +++
 lib/memarea/rte_memarea.c              | 157 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 145 +++++++++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 14 files changed, 417 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 a55b379d73..b9c638221d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1550,6 +1550,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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index de488c7abf..24456604f8 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -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),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index f0886c3bd1..8334ebcbd6 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -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 \
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..b96dad15f6
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -0,0 +1,39 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2022 HiSilicon Limited
+
+Memarea Library
+===============
+
+Introduction
+------------
+
+The memarea library provides an allocator of variable-size objects, it is
+oriented towards the application layer, which could provides 'region-based
+memory management' function [1].
+
+The main features are as follows:
+
+* The default aligement size is ``RTE_CACHE_LINE_SIZE``.
+
+* The memory region can be initialized from the following memory sources:
+  a) RTE memory: e.g. invoke ``rte_malloc_socket`` to obtain. b) System API:
+  e.g. invoke posix_memalign to obtain. c) User provided address: it can be from
+  extendedd memory as long as it is available. d) User provided memarea: it can
+  be from another memarea.
+
+* It provides refcnt feature which could be useful in multi-reader scenario.
+
+* 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.
+
+Reference
+---------
+
+[1] https://en.wikipedia.org/wiki/Region-based_memory_management
diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
index 5d8ef669b8..4c1f760b98 100644
--- a/doc/guides/rel_notes/release_22_11.rst
+++ b/doc/guides/rel_notes/release_22_11.rst
@@ -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, which could provides 'region-based memory
+  management' function.
+
 * **Added configuration for asynchronous flow connection tracking.**
 
   Added connection tracking action number hint to ``rte_flow_configure``
diff --git a/lib/eal/common/eal_common_log.c b/lib/eal/common/eal_common_log.c
index bd7b188ceb..3d62af59c6 100644
--- a/lib/eal/common/eal_common_log.c
+++ b/lib/eal/common/eal_common_log.c
@@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
 	{RTE_LOGTYPE_EFD,        "lib.efd"},
 	{RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
 	{RTE_LOGTYPE_GSO,        "lib.gso"},
+	{RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
 	{RTE_LOGTYPE_USER1,      "user1"},
 	{RTE_LOGTYPE_USER2,      "user2"},
 	{RTE_LOGTYPE_USER3,      "user3"},
diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
index 25ce42cdfc..708f3a39dd 100644
--- a/lib/eal/include/rte_log.h
+++ b/lib/eal/include/rte_log.h
@@ -48,6 +48,7 @@ extern "C" {
 #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
 #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
 #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
+#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..c76392d3e6
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef MEMAREA_PRIVATE_H
+#define MEMAREA_PRIVATE_H
+
+#include <rte_memarea.h>
+
+#define MEMAREA_FREE_ELEM_COOKIE	0xFFFFFFFF
+
+struct memarea_elem {
+	size_t   size;
+	uint32_t cookie;
+	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
+	TAILQ_ENTRY(memarea_elem) elem_node;
+	TAILQ_ENTRY(memarea_elem) free_node;
+} __rte_cache_aligned;
+
+TAILQ_HEAD(memarea_elem_list, memarea_elem);
+
+struct rte_memarea {
+	struct rte_memarea_param init;
+	rte_spinlock_t           lock;
+	void                    *area_addr;
+	struct memarea_elem_list elem_list;
+	struct memarea_elem_list free_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..0a74fb4cd1
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2022 HiSilicon Limited
+
+if is_windows
+    build = false
+    reason = 'not supported on Windows'
+    subdir_done()
+endif
+
+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..868da7661d
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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"
+
+static int
+memarea_check_param(const struct rte_memarea_param *init)
+{
+	size_t len;
+
+	if (init == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
+		return -EINVAL;
+	}
+
+	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
+	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
+		return -EINVAL;
+	}
+
+	if (init->source != RTE_MEMAREA_SOURCE_RTE_MEMORY &&
+	    init->source != RTE_MEMAREA_SOURCE_SYSTEM_API &&
+	    init->source != RTE_MEMAREA_SOURCE_USER_ADDR &&
+	    init->source != RTE_MEMAREA_SOURCE_USER_MEMAREA) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
+			init->name, init->source);
+		return -EINVAL;
+	}
+
+	if (init->total_sz <= sizeof(struct memarea_elem)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
+			init->name, init->total_sz);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR && init->user_addr == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR &&
+	    ((uintptr_t)init->user_addr & (RTE_CACHE_LINE_SIZE - 1))) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr should align: %d!\n",
+			init->name, RTE_CACHE_LINE_SIZE);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA && init->user_memarea == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided memarea is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->alg != RTE_MEMAREA_ALG_DEFAULT) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
+			init->name, init->alg);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+memarea_alloc_from_system_api(size_t size)
+{
+	void *ptr = NULL;
+	int ret;
+
+	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+	if (ret)
+		return NULL;
+	return ptr;
+}
+
+static void *
+memarea_alloc_area(const struct rte_memarea_param *init)
+{
+	void *ptr = NULL;
+
+	if (init->source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
+		ptr = rte_malloc_socket(NULL, init->total_sz, RTE_CACHE_LINE_SIZE,
+					init->numa_socket);
+	else if (init->source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		ptr = memarea_alloc_from_system_api(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
+		ptr = init->user_addr;
+
+	if (ptr == NULL)
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
+
+	return ptr;
+}
+
+struct rte_memarea *
+rte_memarea_create(const struct rte_memarea_param *init)
+{
+	struct memarea_elem *elem;
+	struct rte_memarea *ma;
+	void *addr;
+	int ret;
+
+	ret = memarea_check_param(init);
+	if (ret)
+		return NULL;
+
+	addr = memarea_alloc_area(init);
+	if (addr == NULL)
+		return NULL;
+
+	ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
+	if (ma == NULL) {
+		RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
+		return NULL;
+	}
+
+	ma->init = *init;
+	rte_spinlock_init(&ma->lock);
+	TAILQ_INIT(&ma->elem_list);
+	TAILQ_INIT(&ma->free_list);
+	ma->area_addr = addr;
+	elem = addr;
+	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	elem->refcnt = 0;
+	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
+	TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+
+	return ma;
+}
+
+static void
+memarea_free_area(struct rte_memarea *ma)
+{
+	if (ma->init.source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
+		rte_free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		free(ma->area_addr);
+}
+
+void
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+	if (ma == NULL)
+		return;
+	memarea_free_area(ma);
+	rte_free(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
new file mode 100644
index 0000000000..543cda4cac
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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 default alignment size is RTE_CACHE_LINE_SIZE.
+ * - The memory region can be initialized from the following memory sources:
+ *   1. RTE memory: e.g. invoke rte_malloc_socket to obtain.
+ *   2. System API: e.g. invoke posix_memalign to obtain.
+ *   3. User provided address: it can be from extended memory as long as it is
+ *      available. The address must be aligned to RTE_CACHE_LINE_SIZE.
+ *   4) User provided memarea: it can be from another memarea. So we can build
+ *      the following memory management structure:
+ *         \code{.unparsed}
+ *                           -------------
+ *                           | memarea-1 |
+ *                           -------------
+ *                                 |
+ *                                 v
+ *                  -------------------------------
+ *                  |               |             |
+ *                  v               v             v
+ *            -------------   -------------   ----------
+ *            | memarea-2 |   | memarea-3 |   | object |
+ *            -------------   -------------   ----------
+ *         \endcode
+ *      As shown above, the memarea-2/3 both create from memarea-1's memory.
+ * - It provides refcnt feature which could be useful in multi-reader scenario.
+ * - 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_RTE_MEMORY,
+	/** Memory source comes from system API. */
+	RTE_MEMAREA_SOURCE_SYSTEM_API,
+	/** Memory source comes from user-provided address. */
+	RTE_MEMAREA_SOURCE_USER_ADDR,
+	/** Memory source comes from user-provided memarea. */
+	RTE_MEMAREA_SOURCE_USER_MEMAREA,
+};
+
+/**
+ * Memarea memory management algorithm.
+ */
+enum rte_memarea_alg {
+	/* The default management algorithm is a variant of the next fit
+	 * algorithm. It uses a free-list to apply for memory and uses an
+	 * element-list in ascending order of address to support merging
+	 * upon free.
+	 */
+	RTE_MEMAREA_ALG_DEFAULT,
+};
+
+struct rte_memarea;
+
+struct rte_memarea_param {
+	char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
+	enum rte_memarea_source source;  /**< Memory source of memarea. */
+	enum rte_memarea_alg 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_RTE_MEMORY.
+		 */
+		int numa_socket;
+		/** User provided address, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER_ADDR.
+		 * Note: the provided address must align at least
+		 * RTE_CACHE_LINE_SIZE.
+		 */
+		void *user_addr;
+		/** User provided memarea, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER_MEMAREA.
+		 */
+		struct rte_memarea *user_memarea;
+	};
+};
+
+/**
+ * @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 */
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 c648f7d800..521a25d6c0 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v5 02/10] test/memarea: support memarea test
       [not found] ` <20221005040952.8166-1-datshan@qq.com>
  2022-10-05  4:09   ` [PATCH v5 01/10] memarea: " datshan
@ 2022-10-05  4:09   ` datshan
  2022-10-05  4:09   ` [PATCH v5 03/10] memarea: support alloc/free/update-refcnt API datshan
                     ` (7 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  4:09 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports memarea test about API rte_memarea_create and
rte_memarea_destroy.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 149 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 152 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index b9c638221d..bfdb36f1b5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1554,6 +1554,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index d5cad72116..778de8d65d 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -84,6 +84,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -199,6 +200,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..7c3d78652d
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#ifdef RTE_EXEC_ENV_WINDOWS
+static int
+test_memarea(void)
+{
+	printf("memarea not supported on Windows, skipping test\n");
+	return TEST_SKIPPED;
+}
+
+#else
+
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) \
+			printf("%s Failed\n", #test_func); \
+		else \
+			printf("%s Passed\n", #test_func); \
+	} while (0)
+
+static void
+test_memarea_init_def_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "test-memarea");
+	init->source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int i;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_def_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address align invalid */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	for (i = 1; i < RTE_CACHE_LINE_SIZE; i++) {
+		init.user_addr = (void *)((uintptr_t)i);
+		ma = rte_memarea_create(&init);
+		RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	}
+
+	/* test for user memarea NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for alg invalid */
+	test_memarea_init_def_param(&init);
+	init.alg = RTE_MEMAREA_ALG_DEFAULT + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for create with RTE memory */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_RTE_MEMORY;
+	init.numa_socket = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with system API */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with user-address */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_ADDR;
+	init.user_addr = (void *)(((uintptr_t)user_buffer + RTE_CACHE_LINE_SIZE) &
+				  ~(RTE_CACHE_LINE_SIZE - 1));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	return 0;
+}
+
+#endif /* !RTE_EXEC_ENV_WINDOWS */
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v5 03/10] memarea: support alloc/free/update-refcnt API
       [not found] ` <20221005040952.8166-1-datshan@qq.com>
  2022-10-05  4:09   ` [PATCH v5 01/10] memarea: " datshan
  2022-10-05  4:09   ` [PATCH v5 02/10] test/memarea: support memarea test datshan
@ 2022-10-05  4:09   ` datshan
  2022-10-06 19:43     ` Mattias Rönnblom
  2022-10-05  4:09   ` [PATCH v5 04/10] test/memarea: support alloc/free/update-refcnt test datshan
                     ` (6 subsequent siblings)
  9 siblings, 1 reply; 222+ messages in thread
From: datshan @ 2022-10-05  4:09 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  10 ++
 lib/memarea/memarea_private.h         |   3 +
 lib/memarea/rte_memarea.c             | 143 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  56 ++++++++++
 lib/memarea/version.map               |   3 +
 5 files changed, 215 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index b96dad15f6..41bc0a90cd 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,16 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
+The ``rte_memarea_update_refcnt()`` function is used to update the memory
+object's reference count, if the count reaches zero, the memory object will
+be freed to memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index c76392d3e6..98406879b9 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -25,6 +25,9 @@ struct rte_memarea {
 	void                    *area_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
+
+	uint64_t alloc_fails;
+	uint64_t refcnt_check_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 868da7661d..a072f07f20 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -4,6 +4,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -94,6 +95,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 		ptr = memarea_alloc_from_system_api(init->total_sz);
 	else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
 		ptr = init->user_addr;
+	else if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		ptr = rte_memarea_alloc(init->user_memarea, init->total_sz, 0);
 
 	if (ptr == NULL)
 		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
@@ -145,6 +148,8 @@ memarea_free_area(struct rte_memarea *ma)
 		rte_free(ma->area_addr);
 	else if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
 		free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		rte_memarea_free(ma->init.user_memarea, ma->area_addr);
 }
 
 void
@@ -155,3 +160,141 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(ma);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+#define memarea_roundup(val, align) ((((val) + ((align) - 1)) / (align)) * (align))
+
+static inline bool
+memarea_whether_add_node(size_t free_size, size_t need_size)
+{
+	size_t align_size = memarea_roundup(need_size, RTE_CACHE_LINE_SIZE);
+	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
+}
+
+static inline void
+memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
+{
+	size_t align_size = memarea_roundup(need_size, RTE_CACHE_LINE_SIZE);
+	struct memarea_elem *new_elem;
+	new_elem = (struct memarea_elem *)((uintptr_t)elem + sizeof(struct memarea_elem) +
+					   align_size);
+	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	new_elem->refcnt = 0;
+	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
+	TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
+	elem->size = align_size;
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	struct memarea_elem *elem;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0))
+		return NULL;
+
+	memarea_lock(ma);
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->size < size)
+			continue;
+		if (memarea_whether_add_node(elem->size, size))
+			memarea_add_node(ma, elem, size);
+		elem->cookie = cookie;
+		elem->refcnt = 1;
+		TAILQ_REMOVE(&ma->free_list, elem, free_node);
+		ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));
+		break;
+	}
+	if (unlikely(ptr == NULL))
+		ma->alloc_fails++;
+	memarea_unlock(ma);
+
+	return ptr;
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	rte_memarea_update_refcnt(ma, ptr, -1);
+}
+
+static inline void
+memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
+		   struct memarea_elem *next, bool del_next_from_free,
+		   bool add_curr_to_free)
+{
+	curr->size += next->size + sizeof(struct memarea_elem);
+	next->size = 0;
+	next->cookie = 0;
+	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
+	if (del_next_from_free)
+		TAILQ_REMOVE(&ma->free_list, next, free_node);
+	if (add_curr_to_free) {
+		curr->cookie = MEMAREA_FREE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
+	}
+}
+
+static inline void
+memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
+{
+	struct memarea_elem *prev, *next;
+	bool merged = false;
+	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
+	next = TAILQ_NEXT(elem, elem_node);
+	if (prev != NULL && prev->refcnt == 0) {
+		memarea_merge_node(ma, prev, elem, false, false);
+		elem = prev;
+		merged = true;
+	}
+	if (next != NULL && next->refcnt == 0) {
+		memarea_merge_node(ma, elem, next, true, !merged);
+		merged = true;
+	}
+	if (!merged) {
+		elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	}
+}
+
+void
+rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
+{
+	struct memarea_elem *elem = (struct memarea_elem *)((uintptr_t)ptr -
+							    sizeof(struct memarea_elem));
+
+	if (unlikely(ma == NULL || ptr == NULL))
+		return;
+
+	memarea_lock(ma);
+	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
+		RTE_LOG(ERR, MEMAREA,
+			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
+			ma->init.name, elem->cookie, elem->refcnt, value);
+		ma->refcnt_check_fails++;
+		if (elem->refcnt > 0)
+			elem->refcnt += value;
+		memarea_unlock(ma);
+		return;
+	}
+
+	elem->refcnt += value;
+	if (elem->refcnt == 0)
+		memarea_free_elem(ma, elem);
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 543cda4cac..10e0d6ad5a 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -138,6 +138,62 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ * @param cookie
+ *   User-provided footprint which could used to debug memory leak.
+ *
+ * @return
+ *   Non-NULL on success. Otherwise NULL is returned.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be freed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Update memory's refcnt.
+ *
+ * Update one memory object's refcnt.
+ * When refcnt is updated to be zero, the memory object is freed.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be updated refcnt.
+ * @param value
+ *   The value which need be updated.
+ */
+__rte_experimental
+void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..a0026fc5f9 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,11 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
+	rte_memarea_update_refcnt;
 
 	local: *;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v5 04/10] test/memarea: support alloc/free/update-refcnt test
       [not found] ` <20221005040952.8166-1-datshan@qq.com>
                     ` (2 preceding siblings ...)
  2022-10-05  4:09   ` [PATCH v5 03/10] memarea: support alloc/free/update-refcnt API datshan
@ 2022-10-05  4:09   ` datshan
  2022-10-05  4:09   ` [PATCH v5 05/10] memarea: support dump API datshan
                     ` (5 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  4:09 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 150 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 149 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 7c3d78652d..0a54ede4c1 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -41,6 +41,12 @@ test_memarea_init_def_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_allocated_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -106,8 +112,8 @@ static int
 test_memarea_create_destroy(void)
 {
 	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea *ma, *user_ma;
 	struct rte_memarea_param init;
-	struct rte_memarea *ma;
 
 	/* test for create with RTE memory */
 	test_memarea_init_def_param(&init);
@@ -133,6 +139,145 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with user-memarea */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	user_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(user_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.user_memarea = user_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(user_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE - RTE_CACHE_LINE_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	ptr[1] = rte_memarea_alloc(ma, 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test invalid parameters with update-refcnt */
+	rte_memarea_update_refcnt(NULL, (void *)(uintptr_t)1, 0);
+	rte_memarea_update_refcnt(ma, NULL, 0);
+	rte_memarea_update_refcnt(NULL, NULL, 0);
+
+	/* test free with refcnt fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	/* test update refcnt with fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_update_refcnt(ma, ptr, -2);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[ALLOC_MAX_NUM];
+	int i;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1, 0);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, ptr[6]);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -141,6 +286,9 @@ test_memarea(void)
 {
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	return 0;
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v5 05/10] memarea: support dump API
       [not found] ` <20221005040952.8166-1-datshan@qq.com>
                     ` (3 preceding siblings ...)
  2022-10-05  4:09   ` [PATCH v5 04/10] test/memarea: support alloc/free/update-refcnt test datshan
@ 2022-10-05  4:09   ` datshan
  2022-10-05  4:09   ` [PATCH v5 06/10] test/memarea: support dump test datshan
                     ` (4 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  4:09 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +
 lib/memarea/rte_memarea.c             | 85 +++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 21 +++++++
 lib/memarea/version.map               |  1 +
 4 files changed, 110 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 41bc0a90cd..c77012fe44 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -43,6 +43,9 @@ The ``rte_memarea_update_refcnt()`` function is used to update the memory
 object's reference count, if the count reaches zero, the memory object will
 be freed to memarea.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index a072f07f20..b70830d0bb 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2022 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/queue.h>
@@ -298,3 +299,87 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		memarea_free_elem(ma, elem);
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
+		return "rte-memory";
+	else if (source == RTE_MEMAREA_SOURCE_SYSTEM_API)
+		return "system-api";
+	else if (source == RTE_MEMAREA_SOURCE_USER_ADDR)
+		return "user-addr";
+	else if (source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		return "user-memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_alg alg)
+{
+	if (alg == RTE_MEMAREA_ALG_DEFAULT)
+		return "default";
+	else
+		return "unknown";
+}
+
+static uint32_t
+memarea_elem_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+		num++;
+
+	return num;
+}
+
+static uint32_t
+memarea_free_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->free_list, free_node)
+		num++;
+
+	return num;
+}
+
+static void
+memarea_dump_all(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_elem *elem;
+
+	fprintf(f, "  regions:\n");
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
+			elem->size, elem->cookie, elem->refcnt);
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		fprintf(f, "  source-user-memarea: %s\n", ma->init.user_memarea->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
+	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
+	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
+	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (dump_all)
+		memarea_dump_all(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 10e0d6ad5a..10b8229c64 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -194,6 +194,27 @@ void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 __rte_experimental
 void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index a0026fc5f9..d8ddd93c13 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 	rte_memarea_update_refcnt;
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v5 06/10] test/memarea: support dump test
       [not found] ` <20221005040952.8166-1-datshan@qq.com>
                     ` (4 preceding siblings ...)
  2022-10-05  4:09   ` [PATCH v5 05/10] memarea: support dump API datshan
@ 2022-10-05  4:09   ` datshan
  2022-10-05  4:09   ` [PATCH v5 07/10] memarea: support backup memory mechanism datshan
                     ` (3 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  4:09 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports rte_memarea_dump() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 0a54ede4c1..ab360f0265 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -274,7 +274,39 @@ test_memarea_alloc_free(void)
 	rte_memarea_free(ma, ptr[5]);
 
 	/* test free NULL */
-	rte_memarea_free(ma, ptr[6]);
+	rte_memarea_free(ma, NULL);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
 
 	rte_memarea_destroy(ma);
 
@@ -289,6 +321,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 	return 0;
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v5 07/10] memarea: support backup memory mechanism
       [not found] ` <20221005040952.8166-1-datshan@qq.com>
                     ` (5 preceding siblings ...)
  2022-10-05  4:09   ` [PATCH v5 06/10] test/memarea: support dump test datshan
@ 2022-10-05  4:09   ` datshan
  2022-10-06 19:53     ` Mattias Rönnblom
  2022-10-05  4:09   ` [PATCH v5 08/10] test/memarea: support backup memory test datshan
                     ` (2 subsequent siblings)
  9 siblings, 1 reply; 222+ messages in thread
From: datshan @ 2022-10-05  4:09 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports backup memory mechanism, the memarea could use
another memarea as a backup.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +++
 lib/memarea/memarea_private.h         |  2 ++
 lib/memarea/rte_memarea.c             | 22 ++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  7 +++++++
 4 files changed, 34 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index c77012fe44..842d35f77a 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -25,6 +25,9 @@ The main features are as follows:
 
 * It supports MT-safe as long as it's specified at creation time.
 
+* It provides backup memory mechanism, the memarea could use another memarea
+  as a backup.
+
 Library API Overview
 --------------------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 98406879b9..08735ca81f 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -23,11 +23,13 @@ struct rte_memarea {
 	struct rte_memarea_param init;
 	rte_spinlock_t           lock;
 	void                    *area_addr;
+	void                    *top_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
 
 	uint64_t alloc_fails;
 	uint64_t refcnt_check_fails;
+	uint64_t bak_alloc_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index b70830d0bb..f45191aa7f 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -132,6 +132,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
 	TAILQ_INIT(&ma->elem_list);
 	TAILQ_INIT(&ma->free_list);
 	ma->area_addr = addr;
+	ma->top_addr = (void *)((uintptr_t)addr + init->total_sz - 1);
 	elem = addr;
 	elem->size = init->total_sz - sizeof(struct memarea_elem);
 	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
@@ -200,6 +201,15 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
 	elem->size = align_size;
 }
 
+static inline void *
+memarea_alloc_backup(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	void *ptr = rte_memarea_alloc(ma->init.bak_memarea, size, cookie);
+	if (unlikely(ptr == NULL))
+		ma->bak_alloc_fails++;
+	return ptr;
+}
+
 void *
 rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 {
@@ -221,6 +231,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 		ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));
 		break;
 	}
+	if (ptr == NULL && ma->init.bak_memarea != NULL)
+		ptr = memarea_alloc_backup(ma, size, cookie);
 	if (unlikely(ptr == NULL))
 		ma->alloc_fails++;
 	memarea_unlock(ma);
@@ -283,6 +295,12 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		return;
 
 	memarea_lock(ma);
+	if (ptr < ma->area_addr || ptr > ma->top_addr) {
+		rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
+		memarea_unlock(ma);
+		return;
+	}
+
 	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
 		RTE_LOG(ERR, MEMAREA,
 			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
@@ -373,10 +391,14 @@ rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
 	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
 	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
 	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	if (ma->init.bak_memarea)
+		fprintf(f, "  backup-memarea-name: %s\n", ma->init.bak_memarea->init.name);
 	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
 	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
 	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
 	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (ma->init.bak_memarea)
+		fprintf(f, "  backup_alloc_fails: %" PRIu64 "\n", ma->bak_alloc_fails);
 	if (dump_all)
 		memarea_dump_all(ma, f);
 	memarea_unlock(ma);
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 10b8229c64..348febab7f 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -39,6 +39,9 @@
  *   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.
+ * - It provides backup memory mechanism, the memarea could use another memarea
+ *   as a backup. It will attempts to allocate object from backup memarea when
+ *   the current memarea failed to allocate.
  */
 
 #include <stdbool.h>
@@ -105,6 +108,10 @@ struct rte_memarea_param {
 		 */
 		struct rte_memarea *user_memarea;
 	};
+	/** Backup memarea, which is used to handle the scenario where the
+	 * current memarea allocation failure.
+	 */
+	struct rte_memarea *bak_memarea;
 };
 
 /**
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v5 08/10] test/memarea: support backup memory test
       [not found] ` <20221005040952.8166-1-datshan@qq.com>
                     ` (6 preceding siblings ...)
  2022-10-05  4:09   ` [PATCH v5 07/10] memarea: support backup memory mechanism datshan
@ 2022-10-05  4:09   ` datshan
  2022-10-05  4:09   ` [PATCH v5 09/10] memarea: detect memory corruption based on magic datshan
  2022-10-05  4:09   ` [PATCH v5 10/10] test/memarea: support no MT-safe test datshan
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  4:09 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch supports backup memory mechanism test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index ab360f0265..ec3475c354 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -313,6 +313,46 @@ test_memarea_dump(void)
 	return 0;
 }
 
+static int
+test_memarea_backup(void)
+{
+	struct rte_memarea *ma, *bak_ma;
+	struct rte_memarea_param init;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	strcat(init.name, "_backup");
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	bak_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(bak_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE >> 2;
+	init.bak_memarea = bak_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for backup */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 3, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+	rte_memarea_free(ma, ptr);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(bak_ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -322,6 +362,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
+	MEMAREA_TEST_API_RUN(test_memarea_backup);
 	return 0;
 }
 
-- 
2.34.1



^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v5 09/10] memarea: detect memory corruption based on magic
       [not found] ` <20221005040952.8166-1-datshan@qq.com>
                     ` (7 preceding siblings ...)
  2022-10-05  4:09   ` [PATCH v5 08/10] test/memarea: support backup memory test datshan
@ 2022-10-05  4:09   ` datshan
  2022-10-05  4:09   ` [PATCH v5 10/10] test/memarea: support no MT-safe test datshan
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  4:09 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

This patch provides lightweight mechanism for detecting memory
corruption which based on magic field in each element node.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 lib/memarea/memarea_private.h |  2 ++
 lib/memarea/rte_memarea.c     | 29 ++++++++++++++++++++++++++---
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 08735ca81f..4f5393e6f9 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -7,10 +7,12 @@
 
 #include <rte_memarea.h>
 
+#define MEMAREA_ELEM_MAGIC_NUM		0xbeef1234
 #define MEMAREA_FREE_ELEM_COOKIE	0xFFFFFFFF
 
 struct memarea_elem {
 	size_t   size;
+	uint32_t magic;
 	uint32_t cookie;
 	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
 	TAILQ_ENTRY(memarea_elem) elem_node;
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index f45191aa7f..6290e3449a 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -135,6 +135,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
 	ma->top_addr = (void *)((uintptr_t)addr + init->total_sz - 1);
 	elem = addr;
 	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->magic = MEMAREA_ELEM_MAGIC_NUM;
 	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
 	elem->refcnt = 0;
 	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
@@ -194,6 +195,7 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
 	new_elem = (struct memarea_elem *)((uintptr_t)elem + sizeof(struct memarea_elem) +
 					   align_size);
 	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->magic = MEMAREA_ELEM_MAGIC_NUM;
 	new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
 	new_elem->refcnt = 0;
 	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
@@ -221,6 +223,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 
 	memarea_lock(ma);
 	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (unlikely(elem->magic != MEMAREA_ELEM_MAGIC_NUM))
+			break;
 		if (elem->size < size)
 			continue;
 		if (memarea_whether_add_node(elem->size, size))
@@ -253,6 +257,7 @@ memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
 {
 	curr->size += next->size + sizeof(struct memarea_elem);
 	next->size = 0;
+	next->magic = ~MEMAREA_ELEM_MAGIC_NUM;
 	next->cookie = 0;
 	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
 	if (del_next_from_free)
@@ -295,6 +300,13 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		return;
 
 	memarea_lock(ma);
+	if (unlikely(elem->magic != MEMAREA_ELEM_MAGIC_NUM)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail!\n",
+			ma->init.name, elem->magic);
+		memarea_unlock(ma);
+		return;
+	}
+
 	if (ptr < ma->area_addr || ptr > ma->top_addr) {
 		rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
 		memarea_unlock(ma);
@@ -348,8 +360,11 @@ memarea_elem_list_num(struct rte_memarea *ma)
 	struct memarea_elem *elem;
 	uint32_t num = 0;
 
-	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM)
+			break;
 		num++;
+	}
 
 	return num;
 }
@@ -360,8 +375,11 @@ memarea_free_list_num(struct rte_memarea *ma)
 	struct memarea_elem *elem;
 	uint32_t num = 0;
 
-	TAILQ_FOREACH(elem, &ma->free_list, free_node)
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM)
+			break;
 		num++;
+	}
 
 	return num;
 }
@@ -372,9 +390,14 @@ memarea_dump_all(struct rte_memarea *ma, FILE *f)
 	struct memarea_elem *elem;
 
 	fprintf(f, "  regions:\n");
-	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM) {
+			fprintf(f, "    magic: 0x%x check fail!\n", elem->magic);
+			break;
+		}
 		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
 			elem->size, elem->cookie, elem->refcnt);
+	}
 }
 
 int
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v5 10/10] test/memarea: support no MT-safe test
       [not found] ` <20221005040952.8166-1-datshan@qq.com>
                     ` (8 preceding siblings ...)
  2022-10-05  4:09   ` [PATCH v5 09/10] memarea: detect memory corruption based on magic datshan
@ 2022-10-05  4:09   ` datshan
  9 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  4:09 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev, Chengwen Feng

From: Chengwen Feng <fengchengwen@huawei.com>

MT-safe is enabled by default in previous test, this patch adds no
MT-safe test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index ec3475c354..b4012086fc 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -353,6 +353,35 @@ test_memarea_backup(void)
 	return 0;
 }
 
+static int
+test_memarea_no_mt_safe(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_SYSTEM_API;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init.mt_safe = false;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for all API */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	rte_memarea_free(ma, rte_memarea_alloc(ma, 1, 0));
+	rte_memarea_update_refcnt(ma, rte_memarea_alloc(ma, 1, 0), 1);
+	rte_memarea_update_refcnt(ma, rte_memarea_alloc(ma, 1, 0), -1);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -363,6 +392,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
 	MEMAREA_TEST_API_RUN(test_memarea_backup);
+	MEMAREA_TEST_API_RUN(test_memarea_no_mt_safe);
 	return 0;
 }
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v3 00/10] introduce memarea library
  2022-10-03  7:42   ` [PATCH v3 00/10] introduce memarea library David Marchand
@ 2022-10-05  4:19     ` datshan
  0 siblings, 0 replies; 222+ messages in thread
From: datshan @ 2022-10-05  4:19 UTC (permalink / raw)
  To: David Marchand, Chengwen Feng, Morten Brørup, Burakov,
	Anatoly, dmitry.kozliuk, stephen
  Cc: thomas, dev, jerinjacobk

Hi David,

   The v5 (send by datshan@qq.com) is sent to fix it, please have a look.

Thanks.

On 2022/10/3 15:42, David Marchand wrote:
> On Sat, Sep 24, 2022 at 9:56 AM Chengwen Feng <fengchengwen@huawei.com> wrote:
>> The memarea library is an allocator of variable-size object which based
>> on a memory region. The main features are as follows:
>>
>> - The default alignment size is RTE_CACHE_LINE_SIZE.
>>
>> - The memory region can be initialized from the following memory
>>    sources:
>>    1. RTE memory: e.g. invoke rte_malloc_socket to obtain.
>>    2. System API: e.g. invoke posix_memalign to obtain.
>>    3. User provided address: it can be from extended memory as long as
>>       it is available. The address must be aligned to
>>       RTE_CACHE_LINE_SIZE.
>>    4. User provided memarea: it can be from another memarea.
>>
>> - It provides refcnt feature which could be useful in multi-reader
>>    scenario.
>>
>> - It provides backup memory mechanism, the memarea could use another
>>    memarea as a backup.
>>
>> Note:
>> a) The memarea is oriented towards the application layer, which could
>> provides 'region-based memory management' [1] function.
>> b) The eal library also provide memory zone/heap management, but these
>> are tied to huge pages management.
>>
>> [1] https://en.wikipedia.org/wiki/Region-based_memory_management
>>
>> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> This series did not pass through the CI, as its patches are spread
> over different series in patchwork.
> https://patchwork.dpdk.org/project/dpdk/list/?submitter=2146
>
> There was probably something wrong when sending the patches, please
> check your setup and repost them.
>
>
> Thanks.
>

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v5 03/10] memarea: support alloc/free/update-refcnt API
  2022-10-05  4:09   ` [PATCH v5 03/10] memarea: support alloc/free/update-refcnt API datshan
@ 2022-10-06 19:43     ` Mattias Rönnblom
  2022-10-08  8:02       ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: Mattias Rönnblom @ 2022-10-06 19:43 UTC (permalink / raw)
  To: datshan, david.marchand, mb, anatoly.burakov, dmitry.kozliuk,
	jerinjacobk
  Cc: thomas, dev, Chengwen Feng

On 2022-10-05 06:09, datshan wrote:
> From: Chengwen Feng <fengchengwen@huawei.com>
> 
> This patch supports rte_memarea_alloc()/rte_memarea_free()/
> rte_memarea_update_refcnt() API.
> 
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> ---
>   doc/guides/prog_guide/memarea_lib.rst |  10 ++
>   lib/memarea/memarea_private.h         |   3 +
>   lib/memarea/rte_memarea.c             | 143 ++++++++++++++++++++++++++
>   lib/memarea/rte_memarea.h             |  56 ++++++++++
>   lib/memarea/version.map               |   3 +
>   5 files changed, 215 insertions(+)
> 
> diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
> index b96dad15f6..41bc0a90cd 100644
> --- a/doc/guides/prog_guide/memarea_lib.rst
> +++ b/doc/guides/prog_guide/memarea_lib.rst
> @@ -33,6 +33,16 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
>   
>   The ``rte_memarea_destroy()`` function is used to destroy a memarea.
>   
> +The ``rte_memarea_alloc()`` function is used to alloc one memory object from
> +the memarea.
> +
> +The ``rte_memarea_free()`` function is used to free one memory object which
> +allocated by ``rte_memarea_alloc()``.
> +
> +The ``rte_memarea_update_refcnt()`` function is used to update the memory
> +object's reference count, if the count reaches zero, the memory object will
> +be freed to memarea.
> +
>   Reference
>   ---------
>   
> diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
> index c76392d3e6..98406879b9 100644
> --- a/lib/memarea/memarea_private.h
> +++ b/lib/memarea/memarea_private.h
> @@ -25,6 +25,9 @@ struct rte_memarea {
>   	void                    *area_addr;
>   	struct memarea_elem_list elem_list;
>   	struct memarea_elem_list free_list;
> +
> +	uint64_t alloc_fails;
> +	uint64_t refcnt_check_fails;
>   } __rte_cache_aligned;
>   
>   #endif /* MEMAREA_PRIVATE_H */
> diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
> index 868da7661d..a072f07f20 100644
> --- a/lib/memarea/rte_memarea.c
> +++ b/lib/memarea/rte_memarea.c
> @@ -4,6 +4,7 @@
>   
>   #include <stdio.h>
>   #include <stdlib.h>
> +#include <sys/queue.h>
>   
>   #include <rte_common.h>
>   #include <rte_log.h>
> @@ -94,6 +95,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
>   		ptr = memarea_alloc_from_system_api(init->total_sz);
>   	else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
>   		ptr = init->user_addr;
> +	else if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
> +		ptr = rte_memarea_alloc(init->user_memarea, init->total_sz, 0);
>   
>   	if (ptr == NULL)
>   		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
> @@ -145,6 +148,8 @@ memarea_free_area(struct rte_memarea *ma)
>   		rte_free(ma->area_addr);
>   	else if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
>   		free(ma->area_addr);
> +	else if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
> +		rte_memarea_free(ma->init.user_memarea, ma->area_addr);
>   }
>   
>   void
> @@ -155,3 +160,141 @@ rte_memarea_destroy(struct rte_memarea *ma)
>   	memarea_free_area(ma);
>   	rte_free(ma);
>   }
> +
> +static inline void
> +memarea_lock(struct rte_memarea *ma)
> +{
> +	if (ma->init.mt_safe)
> +		rte_spinlock_lock(&ma->lock);
> +}
> +
> +static inline void
> +memarea_unlock(struct rte_memarea *ma)
> +{
> +	if (ma->init.mt_safe)
> +		rte_spinlock_unlock(&ma->lock);
> +}
> +
> +#define memarea_roundup(val, align) ((((val) + ((align) - 1)) / (align)) * (align))
> +

You should be able to use RTE_ALIGN_CEIL() instead of your own roundup 
macro, assuming this function isn't used in a patch I'm yet to review, 
with a potentially non-power-of-2 align parameter value.

If not, change the macro to a function.

> +static inline bool
> +memarea_whether_add_node(size_t free_size, size_t need_size)
> +{
> +	size_t align_size = memarea_roundup(need_size, RTE_CACHE_LINE_SIZE);
> +	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
> +}
> +
> +static inline void
> +memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
> +{
> +	size_t align_size = memarea_roundup(need_size, RTE_CACHE_LINE_SIZE);
> +	struct memarea_elem *new_elem;
> +	new_elem = (struct memarea_elem *)((uintptr_t)elem + sizeof(struct memarea_elem) +
> +					   align_size);

Use RTE_PTR_ADD().

> +	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
> +	new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
> +	new_elem->refcnt = 0;
> +	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
> +	TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
> +	elem->size = align_size;
> +}
> +
> +void *
> +rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
> +{
> +	struct memarea_elem *elem;
> +	void *ptr = NULL;
> +
> +	if (unlikely(ma == NULL || size == 0))
> +		return NULL;
> +
> +	memarea_lock(ma);
> +	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
> +		if (elem->size < size)
> +			continue;
> +		if (memarea_whether_add_node(elem->size, size))
> +			memarea_add_node(ma, elem, size);
> +		elem->cookie = cookie;
> +		elem->refcnt = 1;
> +		TAILQ_REMOVE(&ma->free_list, elem, free_node);
> +		ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));

RTE_PTR_ADD() again.

> +		break;
> +	}
> +	if (unlikely(ptr == NULL))
> +		ma->alloc_fails++;
> +	memarea_unlock(ma);
> +
> +	return ptr;
> +}
> +
> +void
> +rte_memarea_free(struct rte_memarea *ma, void *ptr)
> +{
> +	rte_memarea_update_refcnt(ma, ptr, -1);
> +}
> +
> +static inline void
> +memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
> +		   struct memarea_elem *next, bool del_next_from_free,
> +		   bool add_curr_to_free)
> +{
> +	curr->size += next->size + sizeof(struct memarea_elem);
> +	next->size = 0;
> +	next->cookie = 0;
> +	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
> +	if (del_next_from_free)
> +		TAILQ_REMOVE(&ma->free_list, next, free_node);
> +	if (add_curr_to_free) {
> +		curr->cookie = MEMAREA_FREE_ELEM_COOKIE;
> +		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
> +	}
> +}
> +
> +static inline void
> +memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
> +{
> +	struct memarea_elem *prev, *next;
> +	bool merged = false;
> +	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
> +	next = TAILQ_NEXT(elem, elem_node);
> +	if (prev != NULL && prev->refcnt == 0) {
> +		memarea_merge_node(ma, prev, elem, false, false);
> +		elem = prev;
> +		merged = true;
> +	}
> +	if (next != NULL && next->refcnt == 0) {
> +		memarea_merge_node(ma, elem, next, true, !merged);
> +		merged = true;
> +	}
> +	if (!merged) {
> +		elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
> +		TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
> +	}
> +}
> +
> +void
> +rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
> +{
> +	struct memarea_elem *elem = (struct memarea_elem *)((uintptr_t)ptr -
> +							    sizeof(struct memarea_elem));
> +
> +	if (unlikely(ma == NULL || ptr == NULL))
> +		return;
> +
> +	memarea_lock(ma);
> +	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
> +		RTE_LOG(ERR, MEMAREA,
> +			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
> +			ma->init.name, elem->cookie, elem->refcnt, value);
> +		ma->refcnt_check_fails++;
> +		if (elem->refcnt > 0)
> +			elem->refcnt += value;
> +		memarea_unlock(ma);
> +		return;
> +	}
> +
> +	elem->refcnt += value;
> +	if (elem->refcnt == 0)
> +		memarea_free_elem(ma, elem);
> +	memarea_unlock(ma);
> +}
> diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
> index 543cda4cac..10e0d6ad5a 100644
> --- a/lib/memarea/rte_memarea.h
> +++ b/lib/memarea/rte_memarea.h
> @@ -138,6 +138,62 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
>   __rte_experimental
>   void rte_memarea_destroy(struct rte_memarea *ma);
>   
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Allocate memory from memarea.
> + *
> + * Allocate one memory object from the memarea.
> + *
> + * @param ma
> + *   The pointer of memarea.
> + * @param size
> + *   The memory size to be allocated.
> + * @param cookie
> + *   User-provided footprint which could used to debug memory leak.
> + *
> + * @return
> + *   Non-NULL on success. Otherwise NULL is returned.
> + */
> +__rte_experimental
> +void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Free memory to memarea.
> + *
> + * Free one memory object to the memarea.
> + *
> + * @param ma
> + *   The pointer of memarea.
> + * @param ptr
> + *   The pointer of memory object which need be freed.
> + */
> +__rte_experimental
> +void rte_memarea_free(struct rte_memarea *ma, void *ptr);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Update memory's refcnt.
> + *
> + * Update one memory object's refcnt.
> + * When refcnt is updated to be zero, the memory object is freed.
> + *
> + * @param ma
> + *   The pointer of memarea.
> + * @param ptr
> + *   The pointer of memory object which need be updated refcnt.
> + * @param value
> + *   The value which need be updated.
> + */
> +__rte_experimental
> +void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
> +
>   #ifdef __cplusplus
>   }
>   #endif
> diff --git a/lib/memarea/version.map b/lib/memarea/version.map
> index f36a04d7cf..a0026fc5f9 100644
> --- a/lib/memarea/version.map
> +++ b/lib/memarea/version.map
> @@ -1,8 +1,11 @@
>   EXPERIMENTAL {
>   	global:
>   
> +	rte_memarea_alloc;
>   	rte_memarea_create;
>   	rte_memarea_destroy;
> +	rte_memarea_free;
> +	rte_memarea_update_refcnt;
>   
>   	local: *;
>   };

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v5 07/10] memarea: support backup memory mechanism
  2022-10-05  4:09   ` [PATCH v5 07/10] memarea: support backup memory mechanism datshan
@ 2022-10-06 19:53     ` Mattias Rönnblom
  2022-10-08  7:56       ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: Mattias Rönnblom @ 2022-10-06 19:53 UTC (permalink / raw)
  To: datshan, david.marchand, mb, anatoly.burakov, dmitry.kozliuk,
	jerinjacobk
  Cc: thomas, dev, Chengwen Feng

On 2022-10-05 06:09, datshan wrote:
> From: Chengwen Feng <fengchengwen@huawei.com>
> 
> This patch supports backup memory mechanism, the memarea could use
> another memarea as a backup.

Maybe it's worth mentioning what backup means already here.

"This patch adds a memarea backup mechanism, where an allocation request 
which cannot be met by a certain memarea is deferred to its backup memarea."

I assume they can be nested indefinitely?

> 
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> ---
>   doc/guides/prog_guide/memarea_lib.rst |  3 +++
>   lib/memarea/memarea_private.h         |  2 ++
>   lib/memarea/rte_memarea.c             | 22 ++++++++++++++++++++++
>   lib/memarea/rte_memarea.h             |  7 +++++++
>   4 files changed, 34 insertions(+)
> 
> diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
> index c77012fe44..842d35f77a 100644
> --- a/doc/guides/prog_guide/memarea_lib.rst
> +++ b/doc/guides/prog_guide/memarea_lib.rst
> @@ -25,6 +25,9 @@ The main features are as follows:
>   
>   * It supports MT-safe as long as it's specified at creation time.
>   
> +* It provides backup memory mechanism, the memarea could use another memarea
> +  as a backup.
> +
>   Library API Overview
>   --------------------
>   
> diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
> index 98406879b9..08735ca81f 100644
> --- a/lib/memarea/memarea_private.h
> +++ b/lib/memarea/memarea_private.h
> @@ -23,11 +23,13 @@ struct rte_memarea {
>   	struct rte_memarea_param init;
>   	rte_spinlock_t           lock;
>   	void                    *area_addr;
> +	void                    *top_addr;
>   	struct memarea_elem_list elem_list;
>   	struct memarea_elem_list free_list;
>   
>   	uint64_t alloc_fails;
>   	uint64_t refcnt_check_fails;
> +	uint64_t bak_alloc_fails;
>   } __rte_cache_aligned;
>   
>   #endif /* MEMAREA_PRIVATE_H */
> diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
> index b70830d0bb..f45191aa7f 100644
> --- a/lib/memarea/rte_memarea.c
> +++ b/lib/memarea/rte_memarea.c
> @@ -132,6 +132,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
>   	TAILQ_INIT(&ma->elem_list);
>   	TAILQ_INIT(&ma->free_list);
>   	ma->area_addr = addr;
> +	ma->top_addr = (void *)((uintptr_t)addr + init->total_sz - 1);

RTE_PTR_ADD()

>   	elem = addr;
>   	elem->size = init->total_sz - sizeof(struct memarea_elem);
>   	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
> @@ -200,6 +201,15 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
>   	elem->size = align_size;
>   }
>   
> +static inline void *
> +memarea_alloc_backup(struct rte_memarea *ma, size_t size, uint32_t cookie)
> +{
> +	void *ptr = rte_memarea_alloc(ma->init.bak_memarea, size, cookie);
> +	if (unlikely(ptr == NULL))
> +		ma->bak_alloc_fails++;
> +	return ptr;
> +}
> +
>   void *
>   rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
>   {
> @@ -221,6 +231,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
>   		ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));
>   		break;
>   	}
> +	if (ptr == NULL && ma->init.bak_memarea != NULL)

Maybe you want an unlikely() around the above, too. I assume using the 
backup area is an exceptional case.

> +		ptr = memarea_alloc_backup(ma, size, cookie);
>   	if (unlikely(ptr == NULL))
>   		ma->alloc_fails++;
>   	memarea_unlock(ma);
> @@ -283,6 +295,12 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
>   		return;
>   
>   	memarea_lock(ma);
> +	if (ptr < ma->area_addr || ptr > ma->top_addr) {
> +		rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
> +		memarea_unlock(ma);
> +		return;
> +	}
> +
>   	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
>   		RTE_LOG(ERR, MEMAREA,
>   			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
> @@ -373,10 +391,14 @@ rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
>   	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
>   	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
>   	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
> +	if (ma->init.bak_memarea)
> +		fprintf(f, "  backup-memarea-name: %s\n", ma->init.bak_memarea->init.name);
>   	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
>   	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
>   	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
>   	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
> +	if (ma->init.bak_memarea)
> +		fprintf(f, "  backup_alloc_fails: %" PRIu64 "\n", ma->bak_alloc_fails);
>   	if (dump_all)
>   		memarea_dump_all(ma, f);
>   	memarea_unlock(ma);
> diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
> index 10b8229c64..348febab7f 100644
> --- a/lib/memarea/rte_memarea.h
> +++ b/lib/memarea/rte_memarea.h
> @@ -39,6 +39,9 @@
>    *   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.
> + * - It provides backup memory mechanism, the memarea could use another memarea
> + *   as a backup. It will attempts to allocate object from backup memarea when
> + *   the current memarea failed to allocate.
>    */
>   
>   #include <stdbool.h>
> @@ -105,6 +108,10 @@ struct rte_memarea_param {
>   		 */
>   		struct rte_memarea *user_memarea;
>   	};
> +	/** Backup memarea, which is used to handle the scenario where the
> +	 * current memarea allocation failure.
> +	 */
> +	struct rte_memarea *bak_memarea;
>   };
>   
>   /**

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v5 01/10] memarea: introduce memarea library
  2022-10-05  4:09   ` [PATCH v5 01/10] memarea: " datshan
@ 2022-10-06 20:15     ` Mattias Rönnblom
  2022-10-08  7:53       ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: Mattias Rönnblom @ 2022-10-06 20:15 UTC (permalink / raw)
  To: datshan, david.marchand, mb, anatoly.burakov, dmitry.kozliuk,
	jerinjacobk
  Cc: thomas, dev, Chengwen Feng

On 2022-10-05 06:09, datshan wrote:
> From: Chengwen Feng <fengchengwen@huawei.com>
> 
> The memarea library is an allocator of variable-size object which based
> on a memory region.
> 
> This patch provides create/destroy API.
> 
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.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  |  39 ++++++
>   doc/guides/rel_notes/release_22_11.rst |   6 +
>   lib/eal/common/eal_common_log.c        |   1 +
>   lib/eal/include/rte_log.h              |   1 +
>   lib/memarea/memarea_private.h          |  30 +++++
>   lib/memarea/meson.build                |  16 +++
>   lib/memarea/rte_memarea.c              | 157 +++++++++++++++++++++++++
>   lib/memarea/rte_memarea.h              | 145 +++++++++++++++++++++++
>   lib/memarea/version.map                |  12 ++
>   lib/meson.build                        |   1 +
>   14 files changed, 417 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 a55b379d73..b9c638221d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1550,6 +1550,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>
> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> index de488c7abf..24456604f8 100644
> --- a/doc/api/doxy-api-index.md
> +++ b/doc/api/doxy-api-index.md
> @@ -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),
> diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
> index f0886c3bd1..8334ebcbd6 100644
> --- a/doc/api/doxy-api.conf.in
> +++ b/doc/api/doxy-api.conf.in
> @@ -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 \
> diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
> index 8564883018..e9015d65e3 100644
> --- a/doc/guides/prog_guide/index.rst
> +++ b/doc/guides/prog_guide/index.rst
> @@ -37,6 +37,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..b96dad15f6
> --- /dev/null
> +++ b/doc/guides/prog_guide/memarea_lib.rst
> @@ -0,0 +1,39 @@
> +..  SPDX-License-Identifier: BSD-3-Clause
> +    Copyright(c) 2022 HiSilicon Limited
> +
> +Memarea Library
> +===============
> +
> +Introduction
> +------------
> +
> +The memarea library provides an allocator of variable-size objects, it is
> +oriented towards the application layer, which could provides 'region-based
> +memory management' function [1].
> +
> +The main features are as follows:
> +
> +* The default aligement size is ``RTE_CACHE_LINE_SIZE``.
> +
> +* The memory region can be initialized from the following memory sources:
> +  a) RTE memory: e.g. invoke ``rte_malloc_socket`` to obtain. b) System API:
> +  e.g. invoke posix_memalign to obtain. c) User provided address: it can be from
> +  extendedd memory as long as it is available. d) User provided memarea: it can
> +  be from another memarea.
> +
> +* It provides refcnt feature which could be useful in multi-reader scenario.
> +
> +* 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.
> +
> +Reference
> +---------
> +
> +[1] https://en.wikipedia.org/wiki/Region-based_memory_management
> diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
> index 5d8ef669b8..4c1f760b98 100644
> --- a/doc/guides/rel_notes/release_22_11.rst
> +++ b/doc/guides/rel_notes/release_22_11.rst
> @@ -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, which could provides 'region-based memory
> +  management' function.
> +
>   * **Added configuration for asynchronous flow connection tracking.**
>   
>     Added connection tracking action number hint to ``rte_flow_configure``
> diff --git a/lib/eal/common/eal_common_log.c b/lib/eal/common/eal_common_log.c
> index bd7b188ceb..3d62af59c6 100644
> --- a/lib/eal/common/eal_common_log.c
> +++ b/lib/eal/common/eal_common_log.c
> @@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
>   	{RTE_LOGTYPE_EFD,        "lib.efd"},
>   	{RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
>   	{RTE_LOGTYPE_GSO,        "lib.gso"},
> +	{RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
>   	{RTE_LOGTYPE_USER1,      "user1"},
>   	{RTE_LOGTYPE_USER2,      "user2"},
>   	{RTE_LOGTYPE_USER3,      "user3"},
> diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
> index 25ce42cdfc..708f3a39dd 100644
> --- a/lib/eal/include/rte_log.h
> +++ b/lib/eal/include/rte_log.h
> @@ -48,6 +48,7 @@ extern "C" {
>   #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
>   #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
>   #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
> +#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
>   
>   /* these log types can be used in an application */
>   #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
> diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
> new file mode 100644
> index 0000000000..c76392d3e6
> --- /dev/null
> +++ b/lib/memarea/memarea_private.h
> @@ -0,0 +1,30 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2022 HiSilicon Limited
> + */
> +
> +#ifndef MEMAREA_PRIVATE_H
> +#define MEMAREA_PRIVATE_H
> +
> +#include <rte_memarea.h>
> +
> +#define MEMAREA_FREE_ELEM_COOKIE	0xFFFFFFFF
> +
> +struct memarea_elem {
> +	size_t   size;
> +	uint32_t cookie;
> +	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
> +	TAILQ_ENTRY(memarea_elem) elem_node;
> +	TAILQ_ENTRY(memarea_elem) free_node;
> +} __rte_cache_aligned;
> +

Why is the elem type cache line aligned? Need the elem data start be 
cache line aligned?

> +TAILQ_HEAD(memarea_elem_list, memarea_elem);
> +
> +struct rte_memarea {
> +	struct rte_memarea_param init;
> +	rte_spinlock_t           lock;
> +	void                    *area_addr;
> +	struct memarea_elem_list elem_list;
> +	struct memarea_elem_list free_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..0a74fb4cd1
> --- /dev/null
> +++ b/lib/memarea/meson.build
> @@ -0,0 +1,16 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2022 HiSilicon Limited
> +
> +if is_windows
> +    build = false
> +    reason = 'not supported on Windows'
> +    subdir_done()
> +endif
> +
> +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..868da7661d
> --- /dev/null
> +++ b/lib/memarea/rte_memarea.c
> @@ -0,0 +1,157 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2022 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"
> +
> +static int
> +memarea_check_param(const struct rte_memarea_param *init)
> +{
> +	size_t len;
> +
> +	if (init == NULL) {
> +		RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
> +		return -EINVAL;
> +	}
> +
> +	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
> +	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
> +		RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
> +		return -EINVAL;
> +	}
> +
> +	if (init->source != RTE_MEMAREA_SOURCE_RTE_MEMORY &&
> +	    init->source != RTE_MEMAREA_SOURCE_SYSTEM_API &&
> +	    init->source != RTE_MEMAREA_SOURCE_USER_ADDR &&
> +	    init->source != RTE_MEMAREA_SOURCE_USER_MEMAREA) {
> +		RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
> +			init->name, init->source);
> +		return -EINVAL;
> +	}
> +
> +	if (init->total_sz <= sizeof(struct memarea_elem)) {
> +		RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
> +			init->name, init->total_sz);
> +		return -EINVAL;
> +	}
> +
> +	if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR && init->user_addr == NULL) {
> +		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr is NULL!\n", init->name);
> +		return -EINVAL;
> +	}
> +
> +	if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR &&
> +	    ((uintptr_t)init->user_addr & (RTE_CACHE_LINE_SIZE - 1))) {
> +		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr should align: %d!\n",
> +			init->name, RTE_CACHE_LINE_SIZE);
> +		return -EINVAL;
> +	}
> +
> +	if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA && init->user_memarea == NULL) {
> +		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided memarea is NULL!\n", init->name);
> +		return -EINVAL;
> +	}
> +
> +	if (init->alg != RTE_MEMAREA_ALG_DEFAULT) {
> +		RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
> +			init->name, init->alg);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void *
> +memarea_alloc_from_system_api(size_t size)
> +{
> +	void *ptr = NULL;
> +	int ret;
> +
> +	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
> +	if (ret)
> +		return NULL;
> +	return ptr;
> +}
> +
> +static void *
> +memarea_alloc_area(const struct rte_memarea_param *init)
> +{
> +	void *ptr = NULL;
> +
> +	if (init->source == RTE_MEMAREA_SOURCE_RTE_MEMORY)

Delete MEMORY. Of course it's memory. What else? If you want to make it 
clear it's from the RTE heap, it should spell out HEAP. Or MALLOC.

> +		ptr = rte_malloc_socket(NULL, init->total_sz, RTE_CACHE_LINE_SIZE,
> +					init->numa_socket);
> +	else if (init->source == RTE_MEMAREA_SOURCE_SYSTEM_API)
> +		ptr = memarea_alloc_from_system_api(init->total_sz);

"SYSTEM_API" doesn't strike me as a good name.

RTE_MEMAREA_SOURCE_LIBC
RTE_MEMAREA_SOURCE_STD_HEAP
or at least remove API so it'll be
RTE_MEMAREA_SOURCE_SYSTEM

> +	else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)

I would delete "ADDR".

> +		ptr = init->user_addr;
> +
> +	if (ptr == NULL)
> +		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
> +
> +	return ptr;
> +}
> +
> +struct rte_memarea *
> +rte_memarea_create(const struct rte_memarea_param *init)
> +{
> +	struct memarea_elem *elem;
> +	struct rte_memarea *ma;
> +	void *addr;
> +	int ret;
> +
> +	ret = memarea_check_param(init);
> +	if (ret)
> +		return NULL;
> +
> +	addr = memarea_alloc_area(init);
> +	if (addr == NULL)
> +		return NULL;
> +
> +	ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
> +	if (ma == NULL) {
> +		RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
> +		return NULL;
> +	}
> +
> +	ma->init = *init;
> +	rte_spinlock_init(&ma->lock);
> +	TAILQ_INIT(&ma->elem_list);
> +	TAILQ_INIT(&ma->free_list);
> +	ma->area_addr = addr;
> +	elem = addr;
> +	elem->size = init->total_sz - sizeof(struct memarea_elem);
> +	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
> +	elem->refcnt = 0;
> +	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
> +	TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
> +
> +	return ma;
> +}
> +
> +static void
> +memarea_free_area(struct rte_memarea *ma)
> +{
> +	if (ma->init.source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
> +		rte_free(ma->area_addr);
> +	else if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
> +		free(ma->area_addr);
> +}
> +
> +void
> +rte_memarea_destroy(struct rte_memarea *ma)
> +{
> +	if (ma == NULL)
> +		return;
> +	memarea_free_area(ma);
> +	rte_free(ma);
> +}
> diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
> new file mode 100644
> index 0000000000..543cda4cac
> --- /dev/null
> +++ b/lib/memarea/rte_memarea.h
> @@ -0,0 +1,145 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2022 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 default alignment size is RTE_CACHE_LINE_SIZE.

This can be read as two things: the object size is aligned, or the start 
address is aligned.

> + * - The memory region can be initialized from the following memory sources:
> + *   1. RTE memory: e.g. invoke rte_malloc_socket to obtain.

Remove "to obtain", or add "memory" after "obtain". Do you really mean 
"e.g.", and not "i.e."?

> + *   2. System API: e.g. invoke posix_memalign to obtain.
> + *   3. User provided address: it can be from extended memory as long as it is
> + *      available. The address must be aligned to RTE_CACHE_LINE_SIZE.

What is extended memory?

> + *   4) User provided memarea: it can be from another memarea. So we can build
> + *      the following memory management structure:
> + *         \code{.unparsed}
> + *                           -------------
> + *                           | memarea-1 |
> + *                           -------------
> + *                                 |
> + *                                 v
> + *                  -------------------------------
> + *                  |               |             |
> + *                  v               v             v
> + *            -------------   -------------   ----------
> + *            | memarea-2 |   | memarea-3 |   | object |
> + *            -------------   -------------   ----------
> + *         \endcode
> + *      As shown above, the memarea-2/3 both create from memarea-1's memory.
> + * - It provides refcnt feature which could be useful in multi-reader scenario.
> + * - 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_RTE_MEMORY,
> +	/** Memory source comes from system API. */
> +	RTE_MEMAREA_SOURCE_SYSTEM_API,
> +	/** Memory source comes from user-provided address. */
> +	RTE_MEMAREA_SOURCE_USER_ADDR,
> +	/** Memory source comes from user-provided memarea. */
> +	RTE_MEMAREA_SOURCE_USER_MEMAREA,
> +};
> +
> +/**
> + * Memarea memory management algorithm.
> + */
> +enum rte_memarea_alg {
> +	/* The default management algorithm is a variant of the next fit
> +	 * algorithm. It uses a free-list to apply for memory and uses an
> +	 * element-list in ascending order of address to support merging
> +	 * upon free.
> +	 */
> +	RTE_MEMAREA_ALG_DEFAULT,
> +};
> +
> +struct rte_memarea;
> +
> +struct rte_memarea_param {

Does this struct need to be public?

> +	char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
> +	enum rte_memarea_source source;  /**< Memory source of memarea. */
> +	enum rte_memarea_alg 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;

Why not bool?

> +	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_RTE_MEMORY.
> +		 */
> +		int numa_socket;
> +		/** User provided address, this field is valid only when the
> +		 * source is set to be RTE_MEMAREA_SOURCE_USER_ADDR.
> +		 * Note: the provided address must align at least
> +		 * RTE_CACHE_LINE_SIZE.
> +		 */
> +		void *user_addr;
> +		/** User provided memarea, this field is valid only when the
> +		 * source is set to be RTE_MEMAREA_SOURCE_USER_MEMAREA.
> +		 */
> +		struct rte_memarea *user_memarea;
> +	};
> +};
> +
> +/**
> + * @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 */
> 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 c648f7d800..521a25d6c0 100644
> --- a/lib/meson.build
> +++ b/lib/meson.build
> @@ -42,6 +42,7 @@ libraries = [
>           'kni',
>           'latencystats',
>           'lpm',
> +        'memarea',
>           'member',
>           'pcapng',
>           'power',

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v5 01/10] memarea: introduce memarea library
  2022-10-06 20:15     ` Mattias Rönnblom
@ 2022-10-08  7:53       ` fengchengwen
  2022-10-10 16:53         ` Mattias Rönnblom
  0 siblings, 1 reply; 222+ messages in thread
From: fengchengwen @ 2022-10-08  7:53 UTC (permalink / raw)
  To: Mattias Rönnblom, datshan, david.marchand, mb,
	anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev

Hi Mattias, Thanks for your review, most will fix in v6.

On 2022/10/7 4:15, Mattias Rönnblom wrote:
> On 2022-10-05 06:09, datshan wrote:
>> From: Chengwen Feng <fengchengwen@huawei.com>
>>
>> The memarea library is an allocator of variable-size object which based
>> on a memory region.
>>
>> This patch provides create/destroy API.
>>
>> Signed-off-by: Chengwen Feng <fengchengwen@huawei.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  |  39 ++++++
>>   doc/guides/rel_notes/release_22_11.rst |   6 +
>>   lib/eal/common/eal_common_log.c        |   1 +
>>   lib/eal/include/rte_log.h              |   1 +
>>   lib/memarea/memarea_private.h          |  30 +++++
>>   lib/memarea/meson.build                |  16 +++
>>   lib/memarea/rte_memarea.c              | 157 +++++++++++++++++++++++++
>>   lib/memarea/rte_memarea.h              | 145 +++++++++++++++++++++++
>>   lib/memarea/version.map                |  12 ++
>>   lib/meson.build                        |   1 +
>>   14 files changed, 417 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 a55b379d73..b9c638221d 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -1550,6 +1550,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>
>> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
>> index de488c7abf..24456604f8 100644
>> --- a/doc/api/doxy-api-index.md
>> +++ b/doc/api/doxy-api-index.md
>> @@ -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),
>> diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
>> index f0886c3bd1..8334ebcbd6 100644
>> --- a/doc/api/doxy-api.conf.in
>> +++ b/doc/api/doxy-api.conf.in
>> @@ -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 \
>> diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
>> index 8564883018..e9015d65e3 100644
>> --- a/doc/guides/prog_guide/index.rst
>> +++ b/doc/guides/prog_guide/index.rst
>> @@ -37,6 +37,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..b96dad15f6
>> --- /dev/null
>> +++ b/doc/guides/prog_guide/memarea_lib.rst
>> @@ -0,0 +1,39 @@
>> +..  SPDX-License-Identifier: BSD-3-Clause
>> +    Copyright(c) 2022 HiSilicon Limited
>> +
>> +Memarea Library
>> +===============
>> +
>> +Introduction
>> +------------
>> +
>> +The memarea library provides an allocator of variable-size objects, it is
>> +oriented towards the application layer, which could provides 'region-based
>> +memory management' function [1].
>> +
>> +The main features are as follows:
>> +
>> +* The default aligement size is ``RTE_CACHE_LINE_SIZE``.
>> +
>> +* The memory region can be initialized from the following memory sources:
>> +  a) RTE memory: e.g. invoke ``rte_malloc_socket`` to obtain. b) System API:
>> +  e.g. invoke posix_memalign to obtain. c) User provided address: it can be from
>> +  extendedd memory as long as it is available. d) User provided memarea: it can
>> +  be from another memarea.
>> +
>> +* It provides refcnt feature which could be useful in multi-reader scenario.
>> +
>> +* 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.
>> +
>> +Reference
>> +---------
>> +
>> +[1] https://en.wikipedia.org/wiki/Region-based_memory_management
>> diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
>> index 5d8ef669b8..4c1f760b98 100644
>> --- a/doc/guides/rel_notes/release_22_11.rst
>> +++ b/doc/guides/rel_notes/release_22_11.rst
>> @@ -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, which could provides 'region-based memory
>> +  management' function.
>> +
>>   * **Added configuration for asynchronous flow connection tracking.**
>>       Added connection tracking action number hint to ``rte_flow_configure``
>> diff --git a/lib/eal/common/eal_common_log.c b/lib/eal/common/eal_common_log.c
>> index bd7b188ceb..3d62af59c6 100644
>> --- a/lib/eal/common/eal_common_log.c
>> +++ b/lib/eal/common/eal_common_log.c
>> @@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
>>       {RTE_LOGTYPE_EFD,        "lib.efd"},
>>       {RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
>>       {RTE_LOGTYPE_GSO,        "lib.gso"},
>> +    {RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
>>       {RTE_LOGTYPE_USER1,      "user1"},
>>       {RTE_LOGTYPE_USER2,      "user2"},
>>       {RTE_LOGTYPE_USER3,      "user3"},
>> diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
>> index 25ce42cdfc..708f3a39dd 100644
>> --- a/lib/eal/include/rte_log.h
>> +++ b/lib/eal/include/rte_log.h
>> @@ -48,6 +48,7 @@ extern "C" {
>>   #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
>>   #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
>>   #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
>> +#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
>>     /* these log types can be used in an application */
>>   #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
>> diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
>> new file mode 100644
>> index 0000000000..c76392d3e6
>> --- /dev/null
>> +++ b/lib/memarea/memarea_private.h
>> @@ -0,0 +1,30 @@
>> +/* SPDX-License-Identifier: BSD-3-Clause
>> + * Copyright(c) 2022 HiSilicon Limited
>> + */
>> +
>> +#ifndef MEMAREA_PRIVATE_H
>> +#define MEMAREA_PRIVATE_H
>> +
>> +#include <rte_memarea.h>
>> +
>> +#define MEMAREA_FREE_ELEM_COOKIE    0xFFFFFFFF
>> +
>> +struct memarea_elem {
>> +    size_t   size;
>> +    uint32_t cookie;
>> +    int32_t  refcnt; /* Non-zero indicates that it has been allocated */
>> +    TAILQ_ENTRY(memarea_elem) elem_node;
>> +    TAILQ_ENTRY(memarea_elem) free_node;
>> +} __rte_cache_aligned;
>> +
> 
> Why is the elem type cache line aligned? Need the elem data start be cache line aligned?

Yes, the elem data align at cache-line default.

> 
>> +TAILQ_HEAD(memarea_elem_list, memarea_elem);
>> +
>> +struct rte_memarea {
>> +    struct rte_memarea_param init;
>> +    rte_spinlock_t           lock;
>> +    void                    *area_addr;
>> +    struct memarea_elem_list elem_list;
>> +    struct memarea_elem_list free_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..0a74fb4cd1
>> --- /dev/null
>> +++ b/lib/memarea/meson.build
>> @@ -0,0 +1,16 @@
>> +# SPDX-License-Identifier: BSD-3-Clause
>> +# Copyright(c) 2022 HiSilicon Limited
>> +
>> +if is_windows
>> +    build = false
>> +    reason = 'not supported on Windows'
>> +    subdir_done()
>> +endif
>> +
>> +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..868da7661d
>> --- /dev/null
>> +++ b/lib/memarea/rte_memarea.c
>> @@ -0,0 +1,157 @@
>> +/* SPDX-License-Identifier: BSD-3-Clause
>> + * Copyright(c) 2022 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"
>> +
>> +static int
>> +memarea_check_param(const struct rte_memarea_param *init)
>> +{
>> +    size_t len;
>> +
>> +    if (init == NULL) {
>> +        RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
>> +        return -EINVAL;
>> +    }
>> +
>> +    len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
>> +    if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
>> +        RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (init->source != RTE_MEMAREA_SOURCE_RTE_MEMORY &&
>> +        init->source != RTE_MEMAREA_SOURCE_SYSTEM_API &&
>> +        init->source != RTE_MEMAREA_SOURCE_USER_ADDR &&
>> +        init->source != RTE_MEMAREA_SOURCE_USER_MEMAREA) {
>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
>> +            init->name, init->source);
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (init->total_sz <= sizeof(struct memarea_elem)) {
>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
>> +            init->name, init->total_sz);
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR && init->user_addr == NULL) {
>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr is NULL!\n", init->name);
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR &&
>> +        ((uintptr_t)init->user_addr & (RTE_CACHE_LINE_SIZE - 1))) {
>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr should align: %d!\n",
>> +            init->name, RTE_CACHE_LINE_SIZE);
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA && init->user_memarea == NULL) {
>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s user provided memarea is NULL!\n", init->name);
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (init->alg != RTE_MEMAREA_ALG_DEFAULT) {
>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
>> +            init->name, init->alg);
>> +        return -EINVAL;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static void *
>> +memarea_alloc_from_system_api(size_t size)
>> +{
>> +    void *ptr = NULL;
>> +    int ret;
>> +
>> +    ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
>> +    if (ret)
>> +        return NULL;
>> +    return ptr;
>> +}
>> +
>> +static void *
>> +memarea_alloc_area(const struct rte_memarea_param *init)
>> +{
>> +    void *ptr = NULL;
>> +
>> +    if (init->source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
> 
> Delete MEMORY. Of course it's memory. What else? If you want to make it clear it's from the RTE heap, it should spell out HEAP. Or MALLOC.

HEAP seem better.

> 
>> +        ptr = rte_malloc_socket(NULL, init->total_sz, RTE_CACHE_LINE_SIZE,
>> +                    init->numa_socket);
>> +    else if (init->source == RTE_MEMAREA_SOURCE_SYSTEM_API)
>> +        ptr = memarea_alloc_from_system_api(init->total_sz);
> 
> "SYSTEM_API" doesn't strike me as a good name.
> 
> RTE_MEMAREA_SOURCE_LIBC

LIBC seem better.

> RTE_MEMAREA_SOURCE_STD_HEAP
> or at least remove API so it'll be
> RTE_MEMAREA_SOURCE_SYSTEM
> 
>> +    else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
> 
> I would delete "ADDR".

+1

> 
>> +        ptr = init->user_addr;
>> +
>> +    if (ptr == NULL)
>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
>> +
>> +    return ptr;
>> +}
>> +
>> +struct rte_memarea *
>> +rte_memarea_create(const struct rte_memarea_param *init)
>> +{
>> +    struct memarea_elem *elem;
>> +    struct rte_memarea *ma;
>> +    void *addr;
>> +    int ret;
>> +
>> +    ret = memarea_check_param(init);
>> +    if (ret)
>> +        return NULL;
>> +
>> +    addr = memarea_alloc_area(init);
>> +    if (addr == NULL)
>> +        return NULL;
>> +
>> +    ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
>> +    if (ma == NULL) {
>> +        RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
>> +        return NULL;
>> +    }
>> +
>> +    ma->init = *init;
>> +    rte_spinlock_init(&ma->lock);
>> +    TAILQ_INIT(&ma->elem_list);
>> +    TAILQ_INIT(&ma->free_list);
>> +    ma->area_addr = addr;
>> +    elem = addr;
>> +    elem->size = init->total_sz - sizeof(struct memarea_elem);
>> +    elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
>> +    elem->refcnt = 0;
>> +    TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
>> +    TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
>> +
>> +    return ma;
>> +}
>> +
>> +static void
>> +memarea_free_area(struct rte_memarea *ma)
>> +{
>> +    if (ma->init.source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
>> +        rte_free(ma->area_addr);
>> +    else if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
>> +        free(ma->area_addr);
>> +}
>> +
>> +void
>> +rte_memarea_destroy(struct rte_memarea *ma)
>> +{
>> +    if (ma == NULL)
>> +        return;
>> +    memarea_free_area(ma);
>> +    rte_free(ma);
>> +}
>> diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
>> new file mode 100644
>> index 0000000000..543cda4cac
>> --- /dev/null
>> +++ b/lib/memarea/rte_memarea.h
>> @@ -0,0 +1,145 @@
>> +/* SPDX-License-Identifier: BSD-3-Clause
>> + * Copyright(c) 2022 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 default alignment size is RTE_CACHE_LINE_SIZE.
> 
> This can be read as two things: the object size is aligned, or the start address is aligned.

It means the start address align, will define more clear in v6.

> 
>> + * - The memory region can be initialized from the following memory sources:
>> + *   1. RTE memory: e.g. invoke rte_malloc_socket to obtain.
> 
> Remove "to obtain", or add "memory" after "obtain". Do you really mean "e.g.", and not "i.e."?

will fix in v6.

> 
>> + *   2. System API: e.g. invoke posix_memalign to obtain.
>> + *   3. User provided address: it can be from extended memory as long as it is
>> + *      available. The address must be aligned to RTE_CACHE_LINE_SIZE.
> 
> What is extended memory?

Like rte_extmen

> 
>> + *   4) User provided memarea: it can be from another memarea. So we can build
>> + *      the following memory management structure:
>> + *         \code{.unparsed}
>> + *                           -------------
>> + *                           | memarea-1 |
>> + *                           -------------
>> + *                                 |
>> + *                                 v
>> + *                  -------------------------------
>> + *                  |               |             |
>> + *                  v               v             v
>> + *            -------------   -------------   ----------
>> + *            | memarea-2 |   | memarea-3 |   | object |
>> + *            -------------   -------------   ----------
>> + *         \endcode
>> + *      As shown above, the memarea-2/3 both create from memarea-1's memory.
>> + * - It provides refcnt feature which could be useful in multi-reader scenario.
>> + * - 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_RTE_MEMORY,
>> +    /** Memory source comes from system API. */
>> +    RTE_MEMAREA_SOURCE_SYSTEM_API,
>> +    /** Memory source comes from user-provided address. */
>> +    RTE_MEMAREA_SOURCE_USER_ADDR,
>> +    /** Memory source comes from user-provided memarea. */
>> +    RTE_MEMAREA_SOURCE_USER_MEMAREA,
>> +};
>> +
>> +/**
>> + * Memarea memory management algorithm.
>> + */
>> +enum rte_memarea_alg {
>> +    /* The default management algorithm is a variant of the next fit
>> +     * algorithm. It uses a free-list to apply for memory and uses an
>> +     * element-list in ascending order of address to support merging
>> +     * upon free.
>> +     */
>> +    RTE_MEMAREA_ALG_DEFAULT,
>> +};
>> +
>> +struct rte_memarea;
>> +
>> +struct rte_memarea_param {
> 
> Does this struct need to be public?

Yes, the rte_memarea_param contains create parameters for create memarea which need be public.

And rte_memarea which pointer implementation struction which just a declare here.

> 
>> +    char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
>> +    enum rte_memarea_source source;  /**< Memory source of memarea. */
>> +    enum rte_memarea_alg 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;
> 
> Why not bool?

Use bit field other than bool will provides more reserved field.

> 
>> +    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_RTE_MEMORY.
>> +         */
>> +        int numa_socket;
>> +        /** User provided address, this field is valid only when the
>> +         * source is set to be RTE_MEMAREA_SOURCE_USER_ADDR.
>> +         * Note: the provided address must align at least
>> +         * RTE_CACHE_LINE_SIZE.
>> +         */
>> +        void *user_addr;
>> +        /** User provided memarea, this field is valid only when the
>> +         * source is set to be RTE_MEMAREA_SOURCE_USER_MEMAREA.
>> +         */
>> +        struct rte_memarea *user_memarea;
>> +    };
>> +};
>> +
>> +/**
>> + * @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 */
>> 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 c648f7d800..521a25d6c0 100644
>> --- a/lib/meson.build
>> +++ b/lib/meson.build
>> @@ -42,6 +42,7 @@ libraries = [
>>           'kni',
>>           'latencystats',
>>           'lpm',
>> +        'memarea',
>>           'member',
>>           'pcapng',
>>           'power',
> 
> .

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v5 07/10] memarea: support backup memory mechanism
  2022-10-06 19:53     ` Mattias Rönnblom
@ 2022-10-08  7:56       ` fengchengwen
  0 siblings, 0 replies; 222+ messages in thread
From: fengchengwen @ 2022-10-08  7:56 UTC (permalink / raw)
  To: Mattias Rönnblom, datshan, david.marchand, mb,
	anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev

Hi Mattias,

On 2022/10/7 3:53, Mattias Rönnblom wrote:
> On 2022-10-05 06:09, datshan wrote:
>> From: Chengwen Feng <fengchengwen@huawei.com>
>>
>> This patch supports backup memory mechanism, the memarea could use
>> another memarea as a backup.
> 
> Maybe it's worth mentioning what backup means already here.
> 
> "This patch adds a memarea backup mechanism, where an allocation request which cannot be met by a certain memarea is deferred to its backup memarea."

+1

> 
> I assume they can be nested indefinitely?

Theoretically, yes.
And I'm going to add, to avoid loops

> 
>>
>> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
>> ---
>>   doc/guides/prog_guide/memarea_lib.rst |  3 +++
>>   lib/memarea/memarea_private.h         |  2 ++
>>   lib/memarea/rte_memarea.c             | 22 ++++++++++++++++++++++
>>   lib/memarea/rte_memarea.h             |  7 +++++++
>>   4 files changed, 34 insertions(+)
>>
>> diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
>> index c77012fe44..842d35f77a 100644
>> --- a/doc/guides/prog_guide/memarea_lib.rst
>> +++ b/doc/guides/prog_guide/memarea_lib.rst
>> @@ -25,6 +25,9 @@ The main features are as follows:
>>     * It supports MT-safe as long as it's specified at creation time.
>>   +* It provides backup memory mechanism, the memarea could use another memarea
>> +  as a backup.
>> +
>>   Library API Overview
>>   --------------------
>>   diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
>> index 98406879b9..08735ca81f 100644
>> --- a/lib/memarea/memarea_private.h
>> +++ b/lib/memarea/memarea_private.h
>> @@ -23,11 +23,13 @@ struct rte_memarea {
>>       struct rte_memarea_param init;
>>       rte_spinlock_t           lock;
>>       void                    *area_addr;
>> +    void                    *top_addr;
>>       struct memarea_elem_list elem_list;
>>       struct memarea_elem_list free_list;
>>         uint64_t alloc_fails;
>>       uint64_t refcnt_check_fails;
>> +    uint64_t bak_alloc_fails;
>>   } __rte_cache_aligned;
>>     #endif /* MEMAREA_PRIVATE_H */
>> diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
>> index b70830d0bb..f45191aa7f 100644
>> --- a/lib/memarea/rte_memarea.c
>> +++ b/lib/memarea/rte_memarea.c
>> @@ -132,6 +132,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
>>       TAILQ_INIT(&ma->elem_list);
>>       TAILQ_INIT(&ma->free_list);
>>       ma->area_addr = addr;
>> +    ma->top_addr = (void *)((uintptr_t)addr + init->total_sz - 1);
> 
> RTE_PTR_ADD()
> 
>>       elem = addr;
>>       elem->size = init->total_sz - sizeof(struct memarea_elem);
>>       elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
>> @@ -200,6 +201,15 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
>>       elem->size = align_size;
>>   }
>>   +static inline void *
>> +memarea_alloc_backup(struct rte_memarea *ma, size_t size, uint32_t cookie)
>> +{
>> +    void *ptr = rte_memarea_alloc(ma->init.bak_memarea, size, cookie);
>> +    if (unlikely(ptr == NULL))
>> +        ma->bak_alloc_fails++;
>> +    return ptr;
>> +}
>> +
>>   void *
>>   rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
>>   {
>> @@ -221,6 +231,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
>>           ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));
>>           break;
>>       }
>> +    if (ptr == NULL && ma->init.bak_memarea != NULL)
> 
> Maybe you want an unlikely() around the above, too. I assume using the backup area is an exceptional case.

+1

> 
>> +        ptr = memarea_alloc_backup(ma, size, cookie);
>>       if (unlikely(ptr == NULL))
>>           ma->alloc_fails++;
>>       memarea_unlock(ma);
>> @@ -283,6 +295,12 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
>>           return;
>>         memarea_lock(ma);
>> +    if (ptr < ma->area_addr || ptr > ma->top_addr) {
>> +        rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
>> +        memarea_unlock(ma);
>> +        return;
>> +    }
>> +
>>       if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
>>           RTE_LOG(ERR, MEMAREA,
>>               "memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
>> @@ -373,10 +391,14 @@ rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
>>       fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
>>       fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
>>       fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
>> +    if (ma->init.bak_memarea)
>> +        fprintf(f, "  backup-memarea-name: %s\n", ma->init.bak_memarea->init.name);
>>       fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
>>       fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
>>       fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
>>       fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
>> +    if (ma->init.bak_memarea)
>> +        fprintf(f, "  backup_alloc_fails: %" PRIu64 "\n", ma->bak_alloc_fails);
>>       if (dump_all)
>>           memarea_dump_all(ma, f);
>>       memarea_unlock(ma);
>> diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
>> index 10b8229c64..348febab7f 100644
>> --- a/lib/memarea/rte_memarea.h
>> +++ b/lib/memarea/rte_memarea.h
>> @@ -39,6 +39,9 @@
>>    *   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.
>> + * - It provides backup memory mechanism, the memarea could use another memarea
>> + *   as a backup. It will attempts to allocate object from backup memarea when
>> + *   the current memarea failed to allocate.
>>    */
>>     #include <stdbool.h>
>> @@ -105,6 +108,10 @@ struct rte_memarea_param {
>>            */
>>           struct rte_memarea *user_memarea;
>>       };
>> +    /** Backup memarea, which is used to handle the scenario where the
>> +     * current memarea allocation failure.
>> +     */
>> +    struct rte_memarea *bak_memarea;
>>   };
>>     /**
> 
> .

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v5 03/10] memarea: support alloc/free/update-refcnt API
  2022-10-06 19:43     ` Mattias Rönnblom
@ 2022-10-08  8:02       ` fengchengwen
  0 siblings, 0 replies; 222+ messages in thread
From: fengchengwen @ 2022-10-08  8:02 UTC (permalink / raw)
  To: Mattias Rönnblom, datshan, david.marchand, mb,
	anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev

Hi Mattias,

On 2022/10/7 3:43, Mattias Rönnblom wrote:
> On 2022-10-05 06:09, datshan wrote:
>> From: Chengwen Feng <fengchengwen@huawei.com>
>>
>> This patch supports rte_memarea_alloc()/rte_memarea_free()/
>> rte_memarea_update_refcnt() API.
>>
>> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
>> ---
>>   doc/guides/prog_guide/memarea_lib.rst |  10 ++
>>   lib/memarea/memarea_private.h         |   3 +
>>   lib/memarea/rte_memarea.c             | 143 ++++++++++++++++++++++++++
>>   lib/memarea/rte_memarea.h             |  56 ++++++++++
>>   lib/memarea/version.map               |   3 +
>>   5 files changed, 215 insertions(+)
>>
>> diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
>> index b96dad15f6..41bc0a90cd 100644
>> --- a/doc/guides/prog_guide/memarea_lib.rst
>> +++ b/doc/guides/prog_guide/memarea_lib.rst
>> @@ -33,6 +33,16 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
>>     The ``rte_memarea_destroy()`` function is used to destroy a memarea.
>>   +The ``rte_memarea_alloc()`` function is used to alloc one memory object from
>> +the memarea.
>> +
>> +The ``rte_memarea_free()`` function is used to free one memory object which
>> +allocated by ``rte_memarea_alloc()``.
>> +
>> +The ``rte_memarea_update_refcnt()`` function is used to update the memory
>> +object's reference count, if the count reaches zero, the memory object will
>> +be freed to memarea.
>> +
>>   Reference
>>   ---------
>>   diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
>> index c76392d3e6..98406879b9 100644
>> --- a/lib/memarea/memarea_private.h
>> +++ b/lib/memarea/memarea_private.h
>> @@ -25,6 +25,9 @@ struct rte_memarea {
>>       void                    *area_addr;
>>       struct memarea_elem_list elem_list;
>>       struct memarea_elem_list free_list;
>> +
>> +    uint64_t alloc_fails;
>> +    uint64_t refcnt_check_fails;
>>   } __rte_cache_aligned;
>>     #endif /* MEMAREA_PRIVATE_H */
>> diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
>> index 868da7661d..a072f07f20 100644
>> --- a/lib/memarea/rte_memarea.c
>> +++ b/lib/memarea/rte_memarea.c
>> @@ -4,6 +4,7 @@
>>     #include <stdio.h>
>>   #include <stdlib.h>
>> +#include <sys/queue.h>
>>     #include <rte_common.h>
>>   #include <rte_log.h>
>> @@ -94,6 +95,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
>>           ptr = memarea_alloc_from_system_api(init->total_sz);
>>       else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
>>           ptr = init->user_addr;
>> +    else if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
>> +        ptr = rte_memarea_alloc(init->user_memarea, init->total_sz, 0);
>>         if (ptr == NULL)
>>           RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
>> @@ -145,6 +148,8 @@ memarea_free_area(struct rte_memarea *ma)
>>           rte_free(ma->area_addr);
>>       else if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
>>           free(ma->area_addr);
>> +    else if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
>> +        rte_memarea_free(ma->init.user_memarea, ma->area_addr);
>>   }
>>     void
>> @@ -155,3 +160,141 @@ rte_memarea_destroy(struct rte_memarea *ma)
>>       memarea_free_area(ma);
>>       rte_free(ma);
>>   }
>> +
>> +static inline void
>> +memarea_lock(struct rte_memarea *ma)
>> +{
>> +    if (ma->init.mt_safe)
>> +        rte_spinlock_lock(&ma->lock);
>> +}
>> +
>> +static inline void
>> +memarea_unlock(struct rte_memarea *ma)
>> +{
>> +    if (ma->init.mt_safe)
>> +        rte_spinlock_unlock(&ma->lock);
>> +}
>> +
>> +#define memarea_roundup(val, align) ((((val) + ((align) - 1)) / (align)) * (align))
>> +
> 
> You should be able to use RTE_ALIGN_CEIL() instead of your own roundup macro, assuming this function isn't used in a patch I'm yet to review, with a potentially non-power-of-2 align parameter value.
> 
> If not, change the macro to a function.

Current, the default align size is CACHE_LINE_SIZE, and it's power-of-2, so will use RTE_ALIGN_CEIL in v6.

> 
>> +static inline bool
>> +memarea_whether_add_node(size_t free_size, size_t need_size)
>> +{
>> +    size_t align_size = memarea_roundup(need_size, RTE_CACHE_LINE_SIZE);
>> +    return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
>> +}
>> +
>> +static inline void
>> +memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
>> +{
>> +    size_t align_size = memarea_roundup(need_size, RTE_CACHE_LINE_SIZE);
>> +    struct memarea_elem *new_elem;
>> +    new_elem = (struct memarea_elem *)((uintptr_t)elem + sizeof(struct memarea_elem) +
>> +                       align_size);
> 
> Use RTE_PTR_ADD().
> 
>> +    new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
>> +    new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
>> +    new_elem->refcnt = 0;
>> +    TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
>> +    TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
>> +    elem->size = align_size;
>> +}
>> +
>> +void *
>> +rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
>> +{
>> +    struct memarea_elem *elem;
>> +    void *ptr = NULL;
>> +
>> +    if (unlikely(ma == NULL || size == 0))
>> +        return NULL;
>> +
>> +    memarea_lock(ma);
>> +    TAILQ_FOREACH(elem, &ma->free_list, free_node) {
>> +        if (elem->size < size)
>> +            continue;
>> +        if (memarea_whether_add_node(elem->size, size))
>> +            memarea_add_node(ma, elem, size);
>> +        elem->cookie = cookie;
>> +        elem->refcnt = 1;
>> +        TAILQ_REMOVE(&ma->free_list, elem, free_node);
>> +        ptr = (void *)((uintptr_t)elem + sizeof(struct memarea_elem));
> 
> RTE_PTR_ADD() again.
> 
>> +        break;
>> +    }
>> +    if (unlikely(ptr == NULL))
>> +        ma->alloc_fails++;
>> +    memarea_unlock(ma);
>> +
>> +    return ptr;
>> +}
>> +
>> +void
>> +rte_memarea_free(struct rte_memarea *ma, void *ptr)
>> +{
>> +    rte_memarea_update_refcnt(ma, ptr, -1);
>> +}
>> +
>> +static inline void
>> +memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
>> +           struct memarea_elem *next, bool del_next_from_free,
>> +           bool add_curr_to_free)
>> +{
>> +    curr->size += next->size + sizeof(struct memarea_elem);
>> +    next->size = 0;
>> +    next->cookie = 0;
>> +    TAILQ_REMOVE(&ma->elem_list, next, elem_node);
>> +    if (del_next_from_free)
>> +        TAILQ_REMOVE(&ma->free_list, next, free_node);
>> +    if (add_curr_to_free) {
>> +        curr->cookie = MEMAREA_FREE_ELEM_COOKIE;
>> +        TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
>> +    }
>> +}
>> +
>> +static inline void
>> +memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
>> +{
>> +    struct memarea_elem *prev, *next;
>> +    bool merged = false;
>> +    prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
>> +    next = TAILQ_NEXT(elem, elem_node);
>> +    if (prev != NULL && prev->refcnt == 0) {
>> +        memarea_merge_node(ma, prev, elem, false, false);
>> +        elem = prev;
>> +        merged = true;
>> +    }
>> +    if (next != NULL && next->refcnt == 0) {
>> +        memarea_merge_node(ma, elem, next, true, !merged);
>> +        merged = true;
>> +    }
>> +    if (!merged) {
>> +        elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
>> +        TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
>> +    }
>> +}
>> +
>> +void
>> +rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
>> +{
>> +    struct memarea_elem *elem = (struct memarea_elem *)((uintptr_t)ptr -
>> +                                sizeof(struct memarea_elem));
>> +
>> +    if (unlikely(ma == NULL || ptr == NULL))
>> +        return;
>> +
>> +    memarea_lock(ma);
>> +    if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
>> +        RTE_LOG(ERR, MEMAREA,
>> +            "memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
>> +            ma->init.name, elem->cookie, elem->refcnt, value);
>> +        ma->refcnt_check_fails++;
>> +        if (elem->refcnt > 0)
>> +            elem->refcnt += value;
>> +        memarea_unlock(ma);
>> +        return;
>> +    }
>> +
>> +    elem->refcnt += value;
>> +    if (elem->refcnt == 0)
>> +        memarea_free_elem(ma, elem);
>> +    memarea_unlock(ma);
>> +}
>> diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
>> index 543cda4cac..10e0d6ad5a 100644
>> --- a/lib/memarea/rte_memarea.h
>> +++ b/lib/memarea/rte_memarea.h
>> @@ -138,6 +138,62 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
>>   __rte_experimental
>>   void rte_memarea_destroy(struct rte_memarea *ma);
>>   +/**
>> + * @warning
>> + * @b EXPERIMENTAL: this API may change without prior notice.
>> + *
>> + * Allocate memory from memarea.
>> + *
>> + * Allocate one memory object from the memarea.
>> + *
>> + * @param ma
>> + *   The pointer of memarea.
>> + * @param size
>> + *   The memory size to be allocated.
>> + * @param cookie
>> + *   User-provided footprint which could used to debug memory leak.
>> + *
>> + * @return
>> + *   Non-NULL on success. Otherwise NULL is returned.
>> + */
>> +__rte_experimental
>> +void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
>> +
>> +/**
>> + * @warning
>> + * @b EXPERIMENTAL: this API may change without prior notice.
>> + *
>> + * Free memory to memarea.
>> + *
>> + * Free one memory object to the memarea.
>> + *
>> + * @param ma
>> + *   The pointer of memarea.
>> + * @param ptr
>> + *   The pointer of memory object which need be freed.
>> + */
>> +__rte_experimental
>> +void rte_memarea_free(struct rte_memarea *ma, void *ptr);
>> +
>> +/**
>> + * @warning
>> + * @b EXPERIMENTAL: this API may change without prior notice.
>> + *
>> + * Update memory's refcnt.
>> + *
>> + * Update one memory object's refcnt.
>> + * When refcnt is updated to be zero, the memory object is freed.
>> + *
>> + * @param ma
>> + *   The pointer of memarea.
>> + * @param ptr
>> + *   The pointer of memory object which need be updated refcnt.
>> + * @param value
>> + *   The value which need be updated.
>> + */
>> +__rte_experimental
>> +void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
>> +
>>   #ifdef __cplusplus
>>   }
>>   #endif
>> diff --git a/lib/memarea/version.map b/lib/memarea/version.map
>> index f36a04d7cf..a0026fc5f9 100644
>> --- a/lib/memarea/version.map
>> +++ b/lib/memarea/version.map
>> @@ -1,8 +1,11 @@
>>   EXPERIMENTAL {
>>       global:
>>   +    rte_memarea_alloc;
>>       rte_memarea_create;
>>       rte_memarea_destroy;
>> +    rte_memarea_free;
>> +    rte_memarea_update_refcnt;
>>         local: *;
>>   };
> 
> .

^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v6 00/10] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (9 preceding siblings ...)
       [not found] ` <20221005040952.8166-1-datshan@qq.com>
@ 2022-10-08 11:33 ` Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 01/10] memarea: " Chengwen Feng
                     ` (9 more replies)
  2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
                   ` (12 subsequent siblings)
  23 siblings, 10 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:33 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev, datshan

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- The allocated object aligned at RTE_CACHE_LINE_SIZE default.

- 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. User provided: it can be from e.g. rte_extmem_xxx as long as it is
     available. The memory's start address must be aligned to
     RTE_CACHE_LINE_SIZE.
  4. User provided memarea: it can be from another memarea.

- It provides refcnt feature which could be useful in multi-reader
  scenario.

- It provides backup memory mechanism, the memarea could use another
  memarea as a backup. It will attempts to allocate object from backup
  memarea when the current memarea failed to allocate.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>

Chengwen Feng (10):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc/free/update-refcnt API
  test/memarea: support alloc/free/update-refcnt test
  memarea: support dump API
  test/memarea: support dump test
  memarea: support backup memory mechanism
  test/memarea: support backup memory test
  memarea: detect memory corruption based on magic
  test/memarea: support no MT-safe test

---
v6:
* address Mattias's comments.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 420 ++++++++++++++++++++++++
 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  |  56 ++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  37 +++
 lib/memarea/meson.build                |  16 +
 lib/memarea/rte_memarea.c              | 428 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 234 ++++++++++++++
 lib/memarea/version.map                |  16 +
 lib/meson.build                        |   1 +
 16 files changed, 1228 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v6 01/10] memarea: introduce memarea library
  2022-10-08 11:33 ` [PATCH v6 00/10] introduce memarea library Chengwen Feng
@ 2022-10-08 11:33   ` Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 02/10] test/memarea: support memarea test Chengwen Feng
                     ` (8 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:33 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev, datshan

The memarea library is an allocator of variable-size object which based
on a memory region.

This patch provides create/destroy API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.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  |  39 ++++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  30 +++++
 lib/memarea/meson.build                |  16 +++
 lib/memarea/rte_memarea.c              | 157 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 146 +++++++++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 14 files changed, 418 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 3757ccc3b3..6c1cb637b0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1562,6 +1562,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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index de488c7abf..24456604f8 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -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),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index f0886c3bd1..8334ebcbd6 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -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 \
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..27b280da6e
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -0,0 +1,39 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2022 HiSilicon Limited
+
+Memarea Library
+===============
+
+Introduction
+------------
+
+The memarea library provides an allocator of variable-size objects, it is
+oriented towards the application layer, which could provides 'region-based
+memory management' function [1].
+
+The main features are as follows:
+
+* The allocated object aligned at ``RTE_CACHE_LINE_SIZE`` default.
+
+* The memory region can be initialized from the following memory sources:
+  a) HEAP: e.g. invoke ``rte_malloc_socket``. b) LIBC: e.g. invoke
+  posix_memalign to obtain. c) User provided: it can be from e.g.
+  rte_extmem_xxx as long as it is available. d) User provided memarea: it can
+  be from another memarea.
+
+* It provides refcnt feature which could be useful in multi-reader scenario.
+
+* 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.
+
+Reference
+---------
+
+[1] https://en.wikipedia.org/wiki/Region-based_memory_management
diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
index 1cd5a39e02..b3088ff539 100644
--- a/doc/guides/rel_notes/release_22_11.rst
+++ b/doc/guides/rel_notes/release_22_11.rst
@@ -63,6 +63,12 @@ New Features
   In theory this implementation should work with any target based on
   ``LoongArch`` ISA.
 
+* **Added memarea library.**
+
+  The memarea library is an allocator of variable-size objects, it is oriented
+  towards the application layer, which could provides 'region-based memory
+  management' function.
+
 * **Added ethdev Rx/Tx descriptor dump API.**
 
   Added the ethdev Rx/Tx descriptor dump API which provides functions
diff --git a/lib/eal/common/eal_common_log.c b/lib/eal/common/eal_common_log.c
index bd7b188ceb..3d62af59c6 100644
--- a/lib/eal/common/eal_common_log.c
+++ b/lib/eal/common/eal_common_log.c
@@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
 	{RTE_LOGTYPE_EFD,        "lib.efd"},
 	{RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
 	{RTE_LOGTYPE_GSO,        "lib.gso"},
+	{RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
 	{RTE_LOGTYPE_USER1,      "user1"},
 	{RTE_LOGTYPE_USER2,      "user2"},
 	{RTE_LOGTYPE_USER3,      "user3"},
diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
index 25ce42cdfc..708f3a39dd 100644
--- a/lib/eal/include/rte_log.h
+++ b/lib/eal/include/rte_log.h
@@ -48,6 +48,7 @@ extern "C" {
 #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
 #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
 #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
+#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..c76392d3e6
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef MEMAREA_PRIVATE_H
+#define MEMAREA_PRIVATE_H
+
+#include <rte_memarea.h>
+
+#define MEMAREA_FREE_ELEM_COOKIE	0xFFFFFFFF
+
+struct memarea_elem {
+	size_t   size;
+	uint32_t cookie;
+	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
+	TAILQ_ENTRY(memarea_elem) elem_node;
+	TAILQ_ENTRY(memarea_elem) free_node;
+} __rte_cache_aligned;
+
+TAILQ_HEAD(memarea_elem_list, memarea_elem);
+
+struct rte_memarea {
+	struct rte_memarea_param init;
+	rte_spinlock_t           lock;
+	void                    *area_addr;
+	struct memarea_elem_list elem_list;
+	struct memarea_elem_list free_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..0a74fb4cd1
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2022 HiSilicon Limited
+
+if is_windows
+    build = false
+    reason = 'not supported on Windows'
+    subdir_done()
+endif
+
+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..bb0bfc2149
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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"
+
+static int
+memarea_check_param(const struct rte_memarea_param *init)
+{
+	size_t len;
+
+	if (init == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
+		return -EINVAL;
+	}
+
+	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
+	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
+		return -EINVAL;
+	}
+
+	if (init->source != RTE_MEMAREA_SOURCE_HEAP &&
+	    init->source != RTE_MEMAREA_SOURCE_LIBC &&
+	    init->source != RTE_MEMAREA_SOURCE_USER &&
+	    init->source != RTE_MEMAREA_SOURCE_USER_MEMAREA) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
+			init->name, init->source);
+		return -EINVAL;
+	}
+
+	if (init->total_sz <= sizeof(struct memarea_elem)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
+			init->name, init->total_sz);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER && init->user_addr == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER &&
+	    ((uintptr_t)init->user_addr & (RTE_CACHE_LINE_SIZE - 1))) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr should align: %d!\n",
+			init->name, RTE_CACHE_LINE_SIZE);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA && init->user_memarea == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided memarea is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->alg != RTE_MEMAREA_ALG_DEFAULT) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
+			init->name, init->alg);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+memarea_alloc_from_libc(size_t size)
+{
+	void *ptr = NULL;
+	int ret;
+
+	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+	if (ret)
+		return NULL;
+	return ptr;
+}
+
+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);
+	else if (init->source == RTE_MEMAREA_SOURCE_USER)
+		ptr = init->user_addr;
+
+	if (ptr == NULL)
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
+
+	return ptr;
+}
+
+struct rte_memarea *
+rte_memarea_create(const struct rte_memarea_param *init)
+{
+	struct memarea_elem *elem;
+	struct rte_memarea *ma;
+	void *addr;
+	int ret;
+
+	ret = memarea_check_param(init);
+	if (ret)
+		return NULL;
+
+	addr = memarea_alloc_area(init);
+	if (addr == NULL)
+		return NULL;
+
+	ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
+	if (ma == NULL) {
+		RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
+		return NULL;
+	}
+
+	ma->init = *init;
+	rte_spinlock_init(&ma->lock);
+	TAILQ_INIT(&ma->elem_list);
+	TAILQ_INIT(&ma->free_list);
+	ma->area_addr = addr;
+	elem = addr;
+	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	elem->refcnt = 0;
+	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
+	TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+
+	return ma;
+}
+
+static void
+memarea_free_area(struct rte_memarea *ma)
+{
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		rte_free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
+		free(ma->area_addr);
+}
+
+void
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+	if (ma == NULL)
+		return;
+	memarea_free_area(ma);
+	rte_free(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
new file mode 100644
index 0000000000..f6d1fc2a59
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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 allocated object aligned at RTE_CACHE_LINE_SIZE default.
+ * - 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. User provided: it can be from e.g. rte_extmem_xxx as long as it is
+ *      available. The memory's start address must be aligned to
+ *      RTE_CACHE_LINE_SIZE.
+ *   4) User provided memarea: it can be from another memarea. So we can build
+ *      the following memory management structure:
+ *         \code{.unparsed}
+ *                           -------------
+ *                           | memarea-1 |
+ *                           -------------
+ *                                 |
+ *                                 v
+ *                  -------------------------------
+ *                  |               |             |
+ *                  v               v             v
+ *            -------------   -------------   ----------
+ *            | memarea-2 |   | memarea-3 |   | object |
+ *            -------------   -------------   ----------
+ *         \endcode
+ *      As shown above, the memarea-2/3 both create from memarea-1's memory.
+ * - It provides refcnt feature which could be useful in multi-reader scenario.
+ * - 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 user-provided memory. */
+	RTE_MEMAREA_SOURCE_USER,
+	/** Memory source comes from user-provided memarea. */
+	RTE_MEMAREA_SOURCE_USER_MEMAREA,
+};
+
+/**
+ * Memarea memory management algorithm.
+ */
+enum rte_memarea_alg {
+	/* The default management algorithm is a variant of the next fit
+	 * algorithm. It uses a free-list to apply for memory and uses an
+	 * element-list in ascending order of address to support merging
+	 * upon free.
+	 */
+	RTE_MEMAREA_ALG_DEFAULT,
+};
+
+struct rte_memarea;
+
+struct rte_memarea_param {
+	char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
+	enum rte_memarea_source source;  /**< Memory source of memarea. */
+	enum rte_memarea_alg 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;
+		/** User provided address, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER.
+		 * Note: the provided address must align at least
+		 * RTE_CACHE_LINE_SIZE.
+		 */
+		void *user_addr;
+		/** User provided memarea, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER_MEMAREA.
+		 */
+		struct rte_memarea *user_memarea;
+	};
+};
+
+/**
+ * @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 */
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 c648f7d800..521a25d6c0 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v6 02/10] test/memarea: support memarea test
  2022-10-08 11:33 ` [PATCH v6 00/10] introduce memarea library Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 01/10] memarea: " Chengwen Feng
@ 2022-10-08 11:33   ` Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 03/10] memarea: support alloc/free/update-refcnt API Chengwen Feng
                     ` (7 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:33 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev, datshan

This patch supports memarea test about API rte_memarea_create and
rte_memarea_destroy.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 168 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 171 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 6c1cb637b0..7adeef31bf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1566,6 +1566,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index d5cad72116..778de8d65d 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -84,6 +84,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -199,6 +200,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..6b6612dc82
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#ifdef RTE_EXEC_ENV_WINDOWS
+static int
+test_memarea(void)
+{
+	printf("memarea not supported on Windows, skipping test\n");
+	return TEST_SKIPPED;
+}
+
+#else
+
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) { \
+			printf("%s Failed\n", #test_func); \
+			fails++; \
+		} else { \
+			printf("%s Passed\n", #test_func); \
+		} \
+	} while (0)
+
+static int fails;
+
+static void
+test_memarea_prepare(void)
+{
+	fails = 0;
+}
+
+static int
+test_memarea_retcode(void)
+{
+	return fails > 0 ? -1 : 0;
+}
+
+static void
+test_memarea_init_def_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "test-memarea");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int i;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_def_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address align invalid */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER;
+	for (i = 1; i < RTE_CACHE_LINE_SIZE; i++) {
+		init.user_addr = (void *)((uintptr_t)i);
+		ma = rte_memarea_create(&init);
+		RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	}
+
+	/* test for user memarea NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for alg invalid */
+	test_memarea_init_def_param(&init);
+	init.alg = RTE_MEMAREA_ALG_DEFAULT + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for create with HEAP */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.numa_socket = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with user-provided */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER;
+	init.user_addr = (void *)(((uintptr_t)user_buffer + RTE_CACHE_LINE_SIZE) &
+				  ~(RTE_CACHE_LINE_SIZE - 1));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	test_memarea_prepare();
+
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+
+	return test_memarea_retcode();
+}
+
+#endif /* !RTE_EXEC_ENV_WINDOWS */
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v6 03/10] memarea: support alloc/free/update-refcnt API
  2022-10-08 11:33 ` [PATCH v6 00/10] introduce memarea library Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 01/10] memarea: " Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 02/10] test/memarea: support memarea test Chengwen Feng
@ 2022-10-08 11:33   ` Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 04/10] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
                     ` (6 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:33 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev, datshan

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  10 ++
 lib/memarea/memarea_private.h         |   3 +
 lib/memarea/rte_memarea.c             | 141 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  56 ++++++++++
 lib/memarea/version.map               |   3 +
 5 files changed, 213 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 27b280da6e..97decf27de 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,16 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
+The ``rte_memarea_update_refcnt()`` function is used to update the memory
+object's reference count, if the count reaches zero, the memory object will
+be freed to memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index c76392d3e6..98406879b9 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -25,6 +25,9 @@ struct rte_memarea {
 	void                    *area_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
+
+	uint64_t alloc_fails;
+	uint64_t refcnt_check_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index bb0bfc2149..522092637e 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -4,6 +4,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -94,6 +95,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 		ptr = memarea_alloc_from_libc(init->total_sz);
 	else if (init->source == RTE_MEMAREA_SOURCE_USER)
 		ptr = init->user_addr;
+	else if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		ptr = rte_memarea_alloc(init->user_memarea, init->total_sz, 0);
 
 	if (ptr == NULL)
 		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
@@ -145,6 +148,8 @@ memarea_free_area(struct rte_memarea *ma)
 		rte_free(ma->area_addr);
 	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		rte_memarea_free(ma->init.user_memarea, ma->area_addr);
 }
 
 void
@@ -155,3 +160,139 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(ma);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+static inline bool
+memarea_whether_add_node(size_t free_size, size_t need_size)
+{
+	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
+	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
+}
+
+static inline void
+memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
+{
+	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
+	struct memarea_elem *new_elem;
+	new_elem = (struct memarea_elem *)RTE_PTR_ADD(elem, sizeof(struct memarea_elem) +
+							    align_size);
+	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	new_elem->refcnt = 0;
+	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
+	TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
+	elem->size = align_size;
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	struct memarea_elem *elem;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0))
+		return NULL;
+
+	memarea_lock(ma);
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->size < size)
+			continue;
+		if (memarea_whether_add_node(elem->size, size))
+			memarea_add_node(ma, elem, size);
+		elem->cookie = cookie;
+		elem->refcnt = 1;
+		TAILQ_REMOVE(&ma->free_list, elem, free_node);
+		ptr = RTE_PTR_ADD(elem, sizeof(struct memarea_elem));
+		break;
+	}
+	if (unlikely(ptr == NULL))
+		ma->alloc_fails++;
+	memarea_unlock(ma);
+
+	return ptr;
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	rte_memarea_update_refcnt(ma, ptr, -1);
+}
+
+static inline void
+memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
+		   struct memarea_elem *next, bool del_next_from_free,
+		   bool add_curr_to_free)
+{
+	curr->size += next->size + sizeof(struct memarea_elem);
+	next->size = 0;
+	next->cookie = 0;
+	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
+	if (del_next_from_free)
+		TAILQ_REMOVE(&ma->free_list, next, free_node);
+	if (add_curr_to_free) {
+		curr->cookie = MEMAREA_FREE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
+	}
+}
+
+static inline void
+memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
+{
+	struct memarea_elem *prev, *next;
+	bool merged = false;
+	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
+	next = TAILQ_NEXT(elem, elem_node);
+	if (prev != NULL && prev->refcnt == 0) {
+		memarea_merge_node(ma, prev, elem, false, false);
+		elem = prev;
+		merged = true;
+	}
+	if (next != NULL && next->refcnt == 0) {
+		memarea_merge_node(ma, elem, next, true, !merged);
+		merged = true;
+	}
+	if (!merged) {
+		elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	}
+}
+
+void
+rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
+{
+	struct memarea_elem *elem = (struct memarea_elem *)RTE_PTR_SUB(ptr,
+							    sizeof(struct memarea_elem));
+
+	if (unlikely(ma == NULL || ptr == NULL))
+		return;
+
+	memarea_lock(ma);
+	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
+		RTE_LOG(ERR, MEMAREA,
+			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
+			ma->init.name, elem->cookie, elem->refcnt, value);
+		ma->refcnt_check_fails++;
+		if (elem->refcnt > 0)
+			elem->refcnt += value;
+		memarea_unlock(ma);
+		return;
+	}
+
+	elem->refcnt += value;
+	if (elem->refcnt == 0)
+		memarea_free_elem(ma, elem);
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index f6d1fc2a59..9e25969bf4 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -139,6 +139,62 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ * @param cookie
+ *   User-provided footprint which could used to debug memory leak.
+ *
+ * @return
+ *   Non-NULL on success. Otherwise NULL is returned.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be freed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Update memory's refcnt.
+ *
+ * Update one memory object's refcnt.
+ * When refcnt is updated to be zero, the memory object is freed.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be updated refcnt.
+ * @param value
+ *   The value which need be updated.
+ */
+__rte_experimental
+void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..a0026fc5f9 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,11 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
+	rte_memarea_update_refcnt;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v6 04/10] test/memarea: support alloc/free/update-refcnt test
  2022-10-08 11:33 ` [PATCH v6 00/10] introduce memarea library Chengwen Feng
                     ` (2 preceding siblings ...)
  2022-10-08 11:33   ` [PATCH v6 03/10] memarea: support alloc/free/update-refcnt API Chengwen Feng
@ 2022-10-08 11:33   ` Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 05/10] memarea: support dump API Chengwen Feng
                     ` (5 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:33 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev, datshan

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 150 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 149 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 6b6612dc82..5ace384b8a 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -57,6 +57,12 @@ test_memarea_init_def_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_allocated_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -122,8 +128,8 @@ static int
 test_memarea_create_destroy(void)
 {
 	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea *ma, *user_ma;
 	struct rte_memarea_param init;
-	struct rte_memarea *ma;
 
 	/* test for create with HEAP */
 	test_memarea_init_def_param(&init);
@@ -149,6 +155,145 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with user-memarea */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	user_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(user_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.user_memarea = user_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(user_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE - RTE_CACHE_LINE_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	ptr[1] = rte_memarea_alloc(ma, 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test invalid parameters with update-refcnt */
+	rte_memarea_update_refcnt(NULL, (void *)(uintptr_t)1, 0);
+	rte_memarea_update_refcnt(ma, NULL, 0);
+	rte_memarea_update_refcnt(NULL, NULL, 0);
+
+	/* test free with refcnt fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	/* test update refcnt with fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_update_refcnt(ma, ptr, -2);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[ALLOC_MAX_NUM];
+	int i;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1, 0);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, NULL);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -159,6 +304,9 @@ test_memarea(void)
 
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v6 05/10] memarea: support dump API
  2022-10-08 11:33 ` [PATCH v6 00/10] introduce memarea library Chengwen Feng
                     ` (3 preceding siblings ...)
  2022-10-08 11:33   ` [PATCH v6 04/10] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
@ 2022-10-08 11:33   ` Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 06/10] test/memarea: support dump test Chengwen Feng
                     ` (4 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:33 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev, datshan

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +
 lib/memarea/rte_memarea.c             | 85 +++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 21 +++++++
 lib/memarea/version.map               |  1 +
 4 files changed, 110 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 97decf27de..720d8099e2 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -43,6 +43,9 @@ The ``rte_memarea_update_refcnt()`` function is used to update the memory
 object's reference count, if the count reaches zero, the memory object will
 be freed to memarea.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 522092637e..bc51d2a5ff 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2022 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/queue.h>
@@ -296,3 +297,87 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		memarea_free_elem(ma, elem);
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_USER)
+		return "user";
+	else if (source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		return "user-memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_alg alg)
+{
+	if (alg == RTE_MEMAREA_ALG_DEFAULT)
+		return "default";
+	else
+		return "unknown";
+}
+
+static uint32_t
+memarea_elem_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+		num++;
+
+	return num;
+}
+
+static uint32_t
+memarea_free_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->free_list, free_node)
+		num++;
+
+	return num;
+}
+
+static void
+memarea_dump_all(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_elem *elem;
+
+	fprintf(f, "  regions:\n");
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
+			elem->size, elem->cookie, elem->refcnt);
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		fprintf(f, "  source-user-memarea: %s\n", ma->init.user_memarea->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
+	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
+	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
+	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (dump_all)
+		memarea_dump_all(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 9e25969bf4..4bf2f36c7c 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -195,6 +195,27 @@ void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 __rte_experimental
 void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index a0026fc5f9..d8ddd93c13 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 	rte_memarea_update_refcnt;
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v6 06/10] test/memarea: support dump test
  2022-10-08 11:33 ` [PATCH v6 00/10] introduce memarea library Chengwen Feng
                     ` (4 preceding siblings ...)
  2022-10-08 11:33   ` [PATCH v6 05/10] memarea: support dump API Chengwen Feng
@ 2022-10-08 11:33   ` Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 07/10] memarea: support backup memory mechanism Chengwen Feng
                     ` (3 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:33 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev, datshan

This patch supports rte_memarea_dump() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 5ace384b8a..d159e4f2ef 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -297,6 +297,38 @@ test_memarea_alloc_free(void)
 	return 0;
 }
 
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -307,6 +339,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v6 07/10] memarea: support backup memory mechanism
  2022-10-08 11:33 ` [PATCH v6 00/10] introduce memarea library Chengwen Feng
                     ` (5 preceding siblings ...)
  2022-10-08 11:33   ` [PATCH v6 06/10] test/memarea: support dump test Chengwen Feng
@ 2022-10-08 11:33   ` Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 08/10] test/memarea: support backup memory test Chengwen Feng
                     ` (2 subsequent siblings)
  9 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:33 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev, datshan

This patch adds a memarea backup mechanism, where an allocation request
which cannot be met by the current memarea is deferred to its backup
memarea.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  4 ++++
 lib/memarea/memarea_private.h         |  2 ++
 lib/memarea/rte_memarea.c             | 22 ++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 11 +++++++++++
 4 files changed, 39 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 720d8099e2..feebafabcc 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -25,6 +25,10 @@ The main features are as follows:
 
 * It supports MT-safe as long as it's specified at creation time.
 
+* It provides backup memory mechanism, the memarea could use another memarea
+  as a backup. It will attempts to allocate object from backup memarea when
+  the current memarea failed to allocate.
+
 Library API Overview
 --------------------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 98406879b9..08735ca81f 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -23,11 +23,13 @@ struct rte_memarea {
 	struct rte_memarea_param init;
 	rte_spinlock_t           lock;
 	void                    *area_addr;
+	void                    *top_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
 
 	uint64_t alloc_fails;
 	uint64_t refcnt_check_fails;
+	uint64_t bak_alloc_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index bc51d2a5ff..f95f89b6ec 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -132,6 +132,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
 	TAILQ_INIT(&ma->elem_list);
 	TAILQ_INIT(&ma->free_list);
 	ma->area_addr = addr;
+	ma->top_addr = RTE_PTR_ADD(addr, init->total_sz - 1);
 	elem = addr;
 	elem->size = init->total_sz - sizeof(struct memarea_elem);
 	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
@@ -198,6 +199,15 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
 	elem->size = align_size;
 }
 
+static inline void *
+memarea_alloc_backup(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	void *ptr = rte_memarea_alloc(ma->init.bak_memarea, size, cookie);
+	if (unlikely(ptr == NULL))
+		ma->bak_alloc_fails++;
+	return ptr;
+}
+
 void *
 rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 {
@@ -219,6 +229,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 		ptr = RTE_PTR_ADD(elem, sizeof(struct memarea_elem));
 		break;
 	}
+	if (unlikely(ptr == NULL && ma->init.bak_memarea != NULL))
+		ptr = memarea_alloc_backup(ma, size, cookie);
 	if (unlikely(ptr == NULL))
 		ma->alloc_fails++;
 	memarea_unlock(ma);
@@ -281,6 +293,12 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		return;
 
 	memarea_lock(ma);
+	if (unlikely(ptr < ma->area_addr || ptr > ma->top_addr)) {
+		rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
+		memarea_unlock(ma);
+		return;
+	}
+
 	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
 		RTE_LOG(ERR, MEMAREA,
 			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
@@ -371,10 +389,14 @@ rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
 	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
 	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
 	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	if (ma->init.bak_memarea)
+		fprintf(f, "  backup-memarea-name: %s\n", ma->init.bak_memarea->init.name);
 	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
 	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
 	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
 	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (ma->init.bak_memarea)
+		fprintf(f, "  backup_alloc_fails: %" PRIu64 "\n", ma->bak_alloc_fails);
 	if (dump_all)
 		memarea_dump_all(ma, f);
 	memarea_unlock(ma);
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 4bf2f36c7c..815d0e3d75 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -40,6 +40,13 @@
  *   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.
+ * - It provides backup memory mechanism, the memarea could use another memarea
+ *   as a backup. It will attempts to allocate object from backup memarea when
+ *   the current memarea failed to allocate.
+ *   @note If the backup memarea is set improperly, loops may occur (e.g.
+ *   memarea-1's backup is memarea-2, and memarea-2's backup is memarea-1) and
+ *   the program will hangs, it is the responsibility of the application to
+ *   ensure that the loops do not form.
  */
 
 #include <stdbool.h>
@@ -106,6 +113,10 @@ struct rte_memarea_param {
 		 */
 		struct rte_memarea *user_memarea;
 	};
+	/** Backup memarea, which is used to handle the scenario where the
+	 * current memarea allocation failure.
+	 */
+	struct rte_memarea *bak_memarea;
 };
 
 /**
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v6 08/10] test/memarea: support backup memory test
  2022-10-08 11:33 ` [PATCH v6 00/10] introduce memarea library Chengwen Feng
                     ` (6 preceding siblings ...)
  2022-10-08 11:33   ` [PATCH v6 07/10] memarea: support backup memory mechanism Chengwen Feng
@ 2022-10-08 11:33   ` Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 09/10] memarea: detect memory corruption based on magic Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 10/10] test/memarea: support no MT-safe test Chengwen Feng
  9 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:33 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev, datshan

This patch supports backup memory mechanism test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index d159e4f2ef..28c66b886c 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -329,6 +329,46 @@ test_memarea_dump(void)
 	return 0;
 }
 
+static int
+test_memarea_backup(void)
+{
+	struct rte_memarea *ma, *bak_ma;
+	struct rte_memarea_param init;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	strcat(init.name, "_backup");
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	bak_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(bak_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE >> 2;
+	init.bak_memarea = bak_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for backup */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 3, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+	rte_memarea_free(ma, ptr);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(bak_ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -340,6 +380,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
+	MEMAREA_TEST_API_RUN(test_memarea_backup);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v6 09/10] memarea: detect memory corruption based on magic
  2022-10-08 11:33 ` [PATCH v6 00/10] introduce memarea library Chengwen Feng
                     ` (7 preceding siblings ...)
  2022-10-08 11:33   ` [PATCH v6 08/10] test/memarea: support backup memory test Chengwen Feng
@ 2022-10-08 11:33   ` Chengwen Feng
  2022-10-08 11:33   ` [PATCH v6 10/10] test/memarea: support no MT-safe test Chengwen Feng
  9 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:33 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev, datshan

This patch provides lightweight mechanism for detecting memory
corruption which based on magic field in each element node.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 lib/memarea/memarea_private.h |  2 ++
 lib/memarea/rte_memarea.c     | 29 ++++++++++++++++++++++++++---
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 08735ca81f..4f5393e6f9 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -7,10 +7,12 @@
 
 #include <rte_memarea.h>
 
+#define MEMAREA_ELEM_MAGIC_NUM		0xbeef1234
 #define MEMAREA_FREE_ELEM_COOKIE	0xFFFFFFFF
 
 struct memarea_elem {
 	size_t   size;
+	uint32_t magic;
 	uint32_t cookie;
 	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
 	TAILQ_ENTRY(memarea_elem) elem_node;
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index f95f89b6ec..2983a36801 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -135,6 +135,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
 	ma->top_addr = RTE_PTR_ADD(addr, init->total_sz - 1);
 	elem = addr;
 	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->magic = MEMAREA_ELEM_MAGIC_NUM;
 	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
 	elem->refcnt = 0;
 	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
@@ -192,6 +193,7 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
 	new_elem = (struct memarea_elem *)RTE_PTR_ADD(elem, sizeof(struct memarea_elem) +
 							    align_size);
 	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->magic = MEMAREA_ELEM_MAGIC_NUM;
 	new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
 	new_elem->refcnt = 0;
 	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
@@ -219,6 +221,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 
 	memarea_lock(ma);
 	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (unlikely(elem->magic != MEMAREA_ELEM_MAGIC_NUM))
+			break;
 		if (elem->size < size)
 			continue;
 		if (memarea_whether_add_node(elem->size, size))
@@ -251,6 +255,7 @@ memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
 {
 	curr->size += next->size + sizeof(struct memarea_elem);
 	next->size = 0;
+	next->magic = ~MEMAREA_ELEM_MAGIC_NUM;
 	next->cookie = 0;
 	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
 	if (del_next_from_free)
@@ -293,6 +298,13 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		return;
 
 	memarea_lock(ma);
+	if (unlikely(elem->magic != MEMAREA_ELEM_MAGIC_NUM)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail!\n",
+			ma->init.name, elem->magic);
+		memarea_unlock(ma);
+		return;
+	}
+
 	if (unlikely(ptr < ma->area_addr || ptr > ma->top_addr)) {
 		rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
 		memarea_unlock(ma);
@@ -346,8 +358,11 @@ memarea_elem_list_num(struct rte_memarea *ma)
 	struct memarea_elem *elem;
 	uint32_t num = 0;
 
-	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM)
+			break;
 		num++;
+	}
 
 	return num;
 }
@@ -358,8 +373,11 @@ memarea_free_list_num(struct rte_memarea *ma)
 	struct memarea_elem *elem;
 	uint32_t num = 0;
 
-	TAILQ_FOREACH(elem, &ma->free_list, free_node)
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM)
+			break;
 		num++;
+	}
 
 	return num;
 }
@@ -370,9 +388,14 @@ memarea_dump_all(struct rte_memarea *ma, FILE *f)
 	struct memarea_elem *elem;
 
 	fprintf(f, "  regions:\n");
-	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM) {
+			fprintf(f, "    magic: 0x%x check fail!\n", elem->magic);
+			break;
+		}
 		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
 			elem->size, elem->cookie, elem->refcnt);
+	}
 }
 
 int
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v6 10/10] test/memarea: support no MT-safe test
  2022-10-08 11:33 ` [PATCH v6 00/10] introduce memarea library Chengwen Feng
                     ` (8 preceding siblings ...)
  2022-10-08 11:33   ` [PATCH v6 09/10] memarea: detect memory corruption based on magic Chengwen Feng
@ 2022-10-08 11:33   ` Chengwen Feng
  9 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:33 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev, datshan

MT-safe is enabled by default in previous test, this patch adds no
MT-safe test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 28c66b886c..adadc6dcfc 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -369,6 +369,35 @@ test_memarea_backup(void)
 	return 0;
 }
 
+static int
+test_memarea_no_mt_safe(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init.mt_safe = false;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for all API */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	rte_memarea_free(ma, rte_memarea_alloc(ma, 1, 0));
+	rte_memarea_update_refcnt(ma, rte_memarea_alloc(ma, 1, 0), 1);
+	rte_memarea_update_refcnt(ma, rte_memarea_alloc(ma, 1, 0), -1);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -381,6 +410,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
 	MEMAREA_TEST_API_RUN(test_memarea_backup);
+	MEMAREA_TEST_API_RUN(test_memarea_no_mt_safe);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v7 00/10] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (10 preceding siblings ...)
  2022-10-08 11:33 ` [PATCH v6 00/10] introduce memarea library Chengwen Feng
@ 2022-10-08 11:58 ` Chengwen Feng
  2022-10-08 11:58   ` [PATCH v7 01/10] memarea: " Chengwen Feng
                     ` (10 more replies)
  2022-10-11 12:17 ` [PATCH v8 0/9] " Chengwen Feng
                   ` (11 subsequent siblings)
  23 siblings, 11 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:58 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- The allocated object aligned at RTE_CACHE_LINE_SIZE default.

- 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. User provided: it can be from e.g. rte_extmem_xxx as long as it is
     available. The memory's start address must be aligned to
     RTE_CACHE_LINE_SIZE.
  4. User provided memarea: it can be from another memarea.

- It provides refcnt feature which could be useful in multi-reader
  scenario.

- It provides backup memory mechanism, the memarea could use another
  memarea as a backup. It will attempts to allocate object from backup
  memarea when the current memarea failed to allocate.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>

Chengwen Feng (10):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc/free/update-refcnt API
  test/memarea: support alloc/free/update-refcnt test
  memarea: support dump API
  test/memarea: support dump test
  memarea: support backup memory mechanism
  test/memarea: support backup memory test
  memarea: detect memory corruption based on magic
  test/memarea: support no MT-safe test

---
v6:
* address Mattias's comments.
* repost patches as there are spread over different series in patchwork.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 420 ++++++++++++++++++++++++
 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  |  56 ++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  37 +++
 lib/memarea/meson.build                |  16 +
 lib/memarea/rte_memarea.c              | 428 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 234 ++++++++++++++
 lib/memarea/version.map                |  16 +
 lib/meson.build                        |   1 +
 16 files changed, 1228 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v7 01/10] memarea: introduce memarea library
  2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
@ 2022-10-08 11:58   ` Chengwen Feng
  2022-10-08 11:58   ` [PATCH v7 02/10] test/memarea: support memarea test Chengwen Feng
                     ` (9 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:58 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

The memarea library is an allocator of variable-size object which based
on a memory region.

This patch provides create/destroy API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.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  |  39 ++++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  30 +++++
 lib/memarea/meson.build                |  16 +++
 lib/memarea/rte_memarea.c              | 157 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 146 +++++++++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 14 files changed, 418 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 3757ccc3b3..6c1cb637b0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1562,6 +1562,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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index de488c7abf..24456604f8 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -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),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index f0886c3bd1..8334ebcbd6 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -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 \
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..27b280da6e
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -0,0 +1,39 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2022 HiSilicon Limited
+
+Memarea Library
+===============
+
+Introduction
+------------
+
+The memarea library provides an allocator of variable-size objects, it is
+oriented towards the application layer, which could provides 'region-based
+memory management' function [1].
+
+The main features are as follows:
+
+* The allocated object aligned at ``RTE_CACHE_LINE_SIZE`` default.
+
+* The memory region can be initialized from the following memory sources:
+  a) HEAP: e.g. invoke ``rte_malloc_socket``. b) LIBC: e.g. invoke
+  posix_memalign to obtain. c) User provided: it can be from e.g.
+  rte_extmem_xxx as long as it is available. d) User provided memarea: it can
+  be from another memarea.
+
+* It provides refcnt feature which could be useful in multi-reader scenario.
+
+* 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.
+
+Reference
+---------
+
+[1] https://en.wikipedia.org/wiki/Region-based_memory_management
diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
index aef2d622dd..1bf5b2b8db 100644
--- a/doc/guides/rel_notes/release_22_11.rst
+++ b/doc/guides/rel_notes/release_22_11.rst
@@ -63,6 +63,12 @@ New Features
   In theory this implementation should work with any target based on
   ``LoongArch`` ISA.
 
+* **Added memarea library.**
+
+  The memarea library is an allocator of variable-size objects, it is oriented
+  towards the application layer, which could provides 'region-based memory
+  management' function.
+
 * **Added support for congestion management in ethdev.**
 
   Added new API functions ``rte_eth_cman_config_init()``,
diff --git a/lib/eal/common/eal_common_log.c b/lib/eal/common/eal_common_log.c
index bd7b188ceb..3d62af59c6 100644
--- a/lib/eal/common/eal_common_log.c
+++ b/lib/eal/common/eal_common_log.c
@@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
 	{RTE_LOGTYPE_EFD,        "lib.efd"},
 	{RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
 	{RTE_LOGTYPE_GSO,        "lib.gso"},
+	{RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
 	{RTE_LOGTYPE_USER1,      "user1"},
 	{RTE_LOGTYPE_USER2,      "user2"},
 	{RTE_LOGTYPE_USER3,      "user3"},
diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
index 25ce42cdfc..708f3a39dd 100644
--- a/lib/eal/include/rte_log.h
+++ b/lib/eal/include/rte_log.h
@@ -48,6 +48,7 @@ extern "C" {
 #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
 #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
 #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
+#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..c76392d3e6
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef MEMAREA_PRIVATE_H
+#define MEMAREA_PRIVATE_H
+
+#include <rte_memarea.h>
+
+#define MEMAREA_FREE_ELEM_COOKIE	0xFFFFFFFF
+
+struct memarea_elem {
+	size_t   size;
+	uint32_t cookie;
+	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
+	TAILQ_ENTRY(memarea_elem) elem_node;
+	TAILQ_ENTRY(memarea_elem) free_node;
+} __rte_cache_aligned;
+
+TAILQ_HEAD(memarea_elem_list, memarea_elem);
+
+struct rte_memarea {
+	struct rte_memarea_param init;
+	rte_spinlock_t           lock;
+	void                    *area_addr;
+	struct memarea_elem_list elem_list;
+	struct memarea_elem_list free_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..0a74fb4cd1
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2022 HiSilicon Limited
+
+if is_windows
+    build = false
+    reason = 'not supported on Windows'
+    subdir_done()
+endif
+
+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..bb0bfc2149
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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"
+
+static int
+memarea_check_param(const struct rte_memarea_param *init)
+{
+	size_t len;
+
+	if (init == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
+		return -EINVAL;
+	}
+
+	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
+	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
+		return -EINVAL;
+	}
+
+	if (init->source != RTE_MEMAREA_SOURCE_HEAP &&
+	    init->source != RTE_MEMAREA_SOURCE_LIBC &&
+	    init->source != RTE_MEMAREA_SOURCE_USER &&
+	    init->source != RTE_MEMAREA_SOURCE_USER_MEMAREA) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
+			init->name, init->source);
+		return -EINVAL;
+	}
+
+	if (init->total_sz <= sizeof(struct memarea_elem)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
+			init->name, init->total_sz);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER && init->user_addr == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER &&
+	    ((uintptr_t)init->user_addr & (RTE_CACHE_LINE_SIZE - 1))) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr should align: %d!\n",
+			init->name, RTE_CACHE_LINE_SIZE);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA && init->user_memarea == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user provided memarea is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->alg != RTE_MEMAREA_ALG_DEFAULT) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
+			init->name, init->alg);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+memarea_alloc_from_libc(size_t size)
+{
+	void *ptr = NULL;
+	int ret;
+
+	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+	if (ret)
+		return NULL;
+	return ptr;
+}
+
+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);
+	else if (init->source == RTE_MEMAREA_SOURCE_USER)
+		ptr = init->user_addr;
+
+	if (ptr == NULL)
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
+
+	return ptr;
+}
+
+struct rte_memarea *
+rte_memarea_create(const struct rte_memarea_param *init)
+{
+	struct memarea_elem *elem;
+	struct rte_memarea *ma;
+	void *addr;
+	int ret;
+
+	ret = memarea_check_param(init);
+	if (ret)
+		return NULL;
+
+	addr = memarea_alloc_area(init);
+	if (addr == NULL)
+		return NULL;
+
+	ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
+	if (ma == NULL) {
+		RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
+		return NULL;
+	}
+
+	ma->init = *init;
+	rte_spinlock_init(&ma->lock);
+	TAILQ_INIT(&ma->elem_list);
+	TAILQ_INIT(&ma->free_list);
+	ma->area_addr = addr;
+	elem = addr;
+	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	elem->refcnt = 0;
+	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
+	TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+
+	return ma;
+}
+
+static void
+memarea_free_area(struct rte_memarea *ma)
+{
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		rte_free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
+		free(ma->area_addr);
+}
+
+void
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+	if (ma == NULL)
+		return;
+	memarea_free_area(ma);
+	rte_free(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
new file mode 100644
index 0000000000..f6d1fc2a59
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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 allocated object aligned at RTE_CACHE_LINE_SIZE default.
+ * - 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. User provided: it can be from e.g. rte_extmem_xxx as long as it is
+ *      available. The memory's start address must be aligned to
+ *      RTE_CACHE_LINE_SIZE.
+ *   4) User provided memarea: it can be from another memarea. So we can build
+ *      the following memory management structure:
+ *         \code{.unparsed}
+ *                           -------------
+ *                           | memarea-1 |
+ *                           -------------
+ *                                 |
+ *                                 v
+ *                  -------------------------------
+ *                  |               |             |
+ *                  v               v             v
+ *            -------------   -------------   ----------
+ *            | memarea-2 |   | memarea-3 |   | object |
+ *            -------------   -------------   ----------
+ *         \endcode
+ *      As shown above, the memarea-2/3 both create from memarea-1's memory.
+ * - It provides refcnt feature which could be useful in multi-reader scenario.
+ * - 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 user-provided memory. */
+	RTE_MEMAREA_SOURCE_USER,
+	/** Memory source comes from user-provided memarea. */
+	RTE_MEMAREA_SOURCE_USER_MEMAREA,
+};
+
+/**
+ * Memarea memory management algorithm.
+ */
+enum rte_memarea_alg {
+	/* The default management algorithm is a variant of the next fit
+	 * algorithm. It uses a free-list to apply for memory and uses an
+	 * element-list in ascending order of address to support merging
+	 * upon free.
+	 */
+	RTE_MEMAREA_ALG_DEFAULT,
+};
+
+struct rte_memarea;
+
+struct rte_memarea_param {
+	char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
+	enum rte_memarea_source source;  /**< Memory source of memarea. */
+	enum rte_memarea_alg 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;
+		/** User provided address, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER.
+		 * Note: the provided address must align at least
+		 * RTE_CACHE_LINE_SIZE.
+		 */
+		void *user_addr;
+		/** User provided memarea, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER_MEMAREA.
+		 */
+		struct rte_memarea *user_memarea;
+	};
+};
+
+/**
+ * @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 */
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 c648f7d800..521a25d6c0 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v7 02/10] test/memarea: support memarea test
  2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
  2022-10-08 11:58   ` [PATCH v7 01/10] memarea: " Chengwen Feng
@ 2022-10-08 11:58   ` Chengwen Feng
  2022-10-08 11:58   ` [PATCH v7 03/10] memarea: support alloc/free/update-refcnt API Chengwen Feng
                     ` (8 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:58 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports memarea test about API rte_memarea_create and
rte_memarea_destroy.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 168 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 171 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 6c1cb637b0..7adeef31bf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1566,6 +1566,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index d5cad72116..778de8d65d 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -84,6 +84,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -199,6 +200,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..6b6612dc82
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#ifdef RTE_EXEC_ENV_WINDOWS
+static int
+test_memarea(void)
+{
+	printf("memarea not supported on Windows, skipping test\n");
+	return TEST_SKIPPED;
+}
+
+#else
+
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) { \
+			printf("%s Failed\n", #test_func); \
+			fails++; \
+		} else { \
+			printf("%s Passed\n", #test_func); \
+		} \
+	} while (0)
+
+static int fails;
+
+static void
+test_memarea_prepare(void)
+{
+	fails = 0;
+}
+
+static int
+test_memarea_retcode(void)
+{
+	return fails > 0 ? -1 : 0;
+}
+
+static void
+test_memarea_init_def_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "test-memarea");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int i;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_def_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user address align invalid */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER;
+	for (i = 1; i < RTE_CACHE_LINE_SIZE; i++) {
+		init.user_addr = (void *)((uintptr_t)i);
+		ma = rte_memarea_create(&init);
+		RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	}
+
+	/* test for user memarea NULL */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for alg invalid */
+	test_memarea_init_def_param(&init);
+	init.alg = RTE_MEMAREA_ALG_DEFAULT + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for create with HEAP */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.numa_socket = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with user-provided */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER;
+	init.user_addr = (void *)(((uintptr_t)user_buffer + RTE_CACHE_LINE_SIZE) &
+				  ~(RTE_CACHE_LINE_SIZE - 1));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	test_memarea_prepare();
+
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+
+	return test_memarea_retcode();
+}
+
+#endif /* !RTE_EXEC_ENV_WINDOWS */
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v7 03/10] memarea: support alloc/free/update-refcnt API
  2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
  2022-10-08 11:58   ` [PATCH v7 01/10] memarea: " Chengwen Feng
  2022-10-08 11:58   ` [PATCH v7 02/10] test/memarea: support memarea test Chengwen Feng
@ 2022-10-08 11:58   ` Chengwen Feng
  2022-10-08 11:58   ` [PATCH v7 04/10] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
                     ` (7 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:58 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  10 ++
 lib/memarea/memarea_private.h         |   3 +
 lib/memarea/rte_memarea.c             | 141 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  56 ++++++++++
 lib/memarea/version.map               |   3 +
 5 files changed, 213 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 27b280da6e..97decf27de 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,16 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
+The ``rte_memarea_update_refcnt()`` function is used to update the memory
+object's reference count, if the count reaches zero, the memory object will
+be freed to memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index c76392d3e6..98406879b9 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -25,6 +25,9 @@ struct rte_memarea {
 	void                    *area_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
+
+	uint64_t alloc_fails;
+	uint64_t refcnt_check_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index bb0bfc2149..522092637e 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -4,6 +4,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -94,6 +95,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 		ptr = memarea_alloc_from_libc(init->total_sz);
 	else if (init->source == RTE_MEMAREA_SOURCE_USER)
 		ptr = init->user_addr;
+	else if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		ptr = rte_memarea_alloc(init->user_memarea, init->total_sz, 0);
 
 	if (ptr == NULL)
 		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
@@ -145,6 +148,8 @@ memarea_free_area(struct rte_memarea *ma)
 		rte_free(ma->area_addr);
 	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		rte_memarea_free(ma->init.user_memarea, ma->area_addr);
 }
 
 void
@@ -155,3 +160,139 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(ma);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+static inline bool
+memarea_whether_add_node(size_t free_size, size_t need_size)
+{
+	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
+	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
+}
+
+static inline void
+memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
+{
+	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
+	struct memarea_elem *new_elem;
+	new_elem = (struct memarea_elem *)RTE_PTR_ADD(elem, sizeof(struct memarea_elem) +
+							    align_size);
+	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+	new_elem->refcnt = 0;
+	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
+	TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
+	elem->size = align_size;
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	struct memarea_elem *elem;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0))
+		return NULL;
+
+	memarea_lock(ma);
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->size < size)
+			continue;
+		if (memarea_whether_add_node(elem->size, size))
+			memarea_add_node(ma, elem, size);
+		elem->cookie = cookie;
+		elem->refcnt = 1;
+		TAILQ_REMOVE(&ma->free_list, elem, free_node);
+		ptr = RTE_PTR_ADD(elem, sizeof(struct memarea_elem));
+		break;
+	}
+	if (unlikely(ptr == NULL))
+		ma->alloc_fails++;
+	memarea_unlock(ma);
+
+	return ptr;
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	rte_memarea_update_refcnt(ma, ptr, -1);
+}
+
+static inline void
+memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
+		   struct memarea_elem *next, bool del_next_from_free,
+		   bool add_curr_to_free)
+{
+	curr->size += next->size + sizeof(struct memarea_elem);
+	next->size = 0;
+	next->cookie = 0;
+	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
+	if (del_next_from_free)
+		TAILQ_REMOVE(&ma->free_list, next, free_node);
+	if (add_curr_to_free) {
+		curr->cookie = MEMAREA_FREE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
+	}
+}
+
+static inline void
+memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
+{
+	struct memarea_elem *prev, *next;
+	bool merged = false;
+	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
+	next = TAILQ_NEXT(elem, elem_node);
+	if (prev != NULL && prev->refcnt == 0) {
+		memarea_merge_node(ma, prev, elem, false, false);
+		elem = prev;
+		merged = true;
+	}
+	if (next != NULL && next->refcnt == 0) {
+		memarea_merge_node(ma, elem, next, true, !merged);
+		merged = true;
+	}
+	if (!merged) {
+		elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	}
+}
+
+void
+rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
+{
+	struct memarea_elem *elem = (struct memarea_elem *)RTE_PTR_SUB(ptr,
+							    sizeof(struct memarea_elem));
+
+	if (unlikely(ma == NULL || ptr == NULL))
+		return;
+
+	memarea_lock(ma);
+	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
+		RTE_LOG(ERR, MEMAREA,
+			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
+			ma->init.name, elem->cookie, elem->refcnt, value);
+		ma->refcnt_check_fails++;
+		if (elem->refcnt > 0)
+			elem->refcnt += value;
+		memarea_unlock(ma);
+		return;
+	}
+
+	elem->refcnt += value;
+	if (elem->refcnt == 0)
+		memarea_free_elem(ma, elem);
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index f6d1fc2a59..9e25969bf4 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -139,6 +139,62 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ * @param cookie
+ *   User-provided footprint which could used to debug memory leak.
+ *
+ * @return
+ *   Non-NULL on success. Otherwise NULL is returned.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be freed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Update memory's refcnt.
+ *
+ * Update one memory object's refcnt.
+ * When refcnt is updated to be zero, the memory object is freed.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be updated refcnt.
+ * @param value
+ *   The value which need be updated.
+ */
+__rte_experimental
+void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..a0026fc5f9 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,11 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
+	rte_memarea_update_refcnt;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v7 04/10] test/memarea: support alloc/free/update-refcnt test
  2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
                     ` (2 preceding siblings ...)
  2022-10-08 11:58   ` [PATCH v7 03/10] memarea: support alloc/free/update-refcnt API Chengwen Feng
@ 2022-10-08 11:58   ` Chengwen Feng
  2022-10-08 11:58   ` [PATCH v7 05/10] memarea: support dump API Chengwen Feng
                     ` (6 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:58 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 150 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 149 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 6b6612dc82..5ace384b8a 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -57,6 +57,12 @@ test_memarea_init_def_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_allocated_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -122,8 +128,8 @@ static int
 test_memarea_create_destroy(void)
 {
 	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea *ma, *user_ma;
 	struct rte_memarea_param init;
-	struct rte_memarea *ma;
 
 	/* test for create with HEAP */
 	test_memarea_init_def_param(&init);
@@ -149,6 +155,145 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with user-memarea */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	user_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(user_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.user_memarea = user_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(user_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE - RTE_CACHE_LINE_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	ptr[1] = rte_memarea_alloc(ma, 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test invalid parameters with update-refcnt */
+	rte_memarea_update_refcnt(NULL, (void *)(uintptr_t)1, 0);
+	rte_memarea_update_refcnt(ma, NULL, 0);
+	rte_memarea_update_refcnt(NULL, NULL, 0);
+
+	/* test free with refcnt fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	/* test update refcnt with fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_allocated_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_update_refcnt(ma, ptr, -2);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[ALLOC_MAX_NUM];
+	int i;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1, 0);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, NULL);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -159,6 +304,9 @@ test_memarea(void)
 
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v7 05/10] memarea: support dump API
  2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
                     ` (3 preceding siblings ...)
  2022-10-08 11:58   ` [PATCH v7 04/10] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
@ 2022-10-08 11:58   ` Chengwen Feng
  2022-10-08 11:58   ` [PATCH v7 06/10] test/memarea: support dump test Chengwen Feng
                     ` (5 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:58 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +
 lib/memarea/rte_memarea.c             | 85 +++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 21 +++++++
 lib/memarea/version.map               |  1 +
 4 files changed, 110 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 97decf27de..720d8099e2 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -43,6 +43,9 @@ The ``rte_memarea_update_refcnt()`` function is used to update the memory
 object's reference count, if the count reaches zero, the memory object will
 be freed to memarea.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 522092637e..bc51d2a5ff 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2022 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/queue.h>
@@ -296,3 +297,87 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		memarea_free_elem(ma, elem);
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_USER)
+		return "user";
+	else if (source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		return "user-memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_alg alg)
+{
+	if (alg == RTE_MEMAREA_ALG_DEFAULT)
+		return "default";
+	else
+		return "unknown";
+}
+
+static uint32_t
+memarea_elem_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+		num++;
+
+	return num;
+}
+
+static uint32_t
+memarea_free_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->free_list, free_node)
+		num++;
+
+	return num;
+}
+
+static void
+memarea_dump_all(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_elem *elem;
+
+	fprintf(f, "  regions:\n");
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
+			elem->size, elem->cookie, elem->refcnt);
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_USER_MEMAREA)
+		fprintf(f, "  source-user-memarea: %s\n", ma->init.user_memarea->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
+	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
+	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
+	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (dump_all)
+		memarea_dump_all(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 9e25969bf4..4bf2f36c7c 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -195,6 +195,27 @@ void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 __rte_experimental
 void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index a0026fc5f9..d8ddd93c13 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 	rte_memarea_update_refcnt;
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v7 06/10] test/memarea: support dump test
  2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
                     ` (4 preceding siblings ...)
  2022-10-08 11:58   ` [PATCH v7 05/10] memarea: support dump API Chengwen Feng
@ 2022-10-08 11:58   ` Chengwen Feng
  2022-10-08 11:58   ` [PATCH v7 07/10] memarea: support backup memory mechanism Chengwen Feng
                     ` (4 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:58 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_dump() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 5ace384b8a..d159e4f2ef 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -297,6 +297,38 @@ test_memarea_alloc_free(void)
 	return 0;
 }
 
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -307,6 +339,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v7 07/10] memarea: support backup memory mechanism
  2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
                     ` (5 preceding siblings ...)
  2022-10-08 11:58   ` [PATCH v7 06/10] test/memarea: support dump test Chengwen Feng
@ 2022-10-08 11:58   ` Chengwen Feng
  2022-10-08 11:58   ` [PATCH v7 08/10] test/memarea: support backup memory test Chengwen Feng
                     ` (3 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:58 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch adds a memarea backup mechanism, where an allocation request
which cannot be met by the current memarea is deferred to its backup
memarea.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  4 ++++
 lib/memarea/memarea_private.h         |  2 ++
 lib/memarea/rte_memarea.c             | 22 ++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 11 +++++++++++
 4 files changed, 39 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 720d8099e2..feebafabcc 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -25,6 +25,10 @@ The main features are as follows:
 
 * It supports MT-safe as long as it's specified at creation time.
 
+* It provides backup memory mechanism, the memarea could use another memarea
+  as a backup. It will attempts to allocate object from backup memarea when
+  the current memarea failed to allocate.
+
 Library API Overview
 --------------------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 98406879b9..08735ca81f 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -23,11 +23,13 @@ struct rte_memarea {
 	struct rte_memarea_param init;
 	rte_spinlock_t           lock;
 	void                    *area_addr;
+	void                    *top_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
 
 	uint64_t alloc_fails;
 	uint64_t refcnt_check_fails;
+	uint64_t bak_alloc_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index bc51d2a5ff..f95f89b6ec 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -132,6 +132,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
 	TAILQ_INIT(&ma->elem_list);
 	TAILQ_INIT(&ma->free_list);
 	ma->area_addr = addr;
+	ma->top_addr = RTE_PTR_ADD(addr, init->total_sz - 1);
 	elem = addr;
 	elem->size = init->total_sz - sizeof(struct memarea_elem);
 	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
@@ -198,6 +199,15 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
 	elem->size = align_size;
 }
 
+static inline void *
+memarea_alloc_backup(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	void *ptr = rte_memarea_alloc(ma->init.bak_memarea, size, cookie);
+	if (unlikely(ptr == NULL))
+		ma->bak_alloc_fails++;
+	return ptr;
+}
+
 void *
 rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 {
@@ -219,6 +229,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 		ptr = RTE_PTR_ADD(elem, sizeof(struct memarea_elem));
 		break;
 	}
+	if (unlikely(ptr == NULL && ma->init.bak_memarea != NULL))
+		ptr = memarea_alloc_backup(ma, size, cookie);
 	if (unlikely(ptr == NULL))
 		ma->alloc_fails++;
 	memarea_unlock(ma);
@@ -281,6 +293,12 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		return;
 
 	memarea_lock(ma);
+	if (unlikely(ptr < ma->area_addr || ptr > ma->top_addr)) {
+		rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
+		memarea_unlock(ma);
+		return;
+	}
+
 	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
 		RTE_LOG(ERR, MEMAREA,
 			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
@@ -371,10 +389,14 @@ rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
 	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
 	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
 	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	if (ma->init.bak_memarea)
+		fprintf(f, "  backup-memarea-name: %s\n", ma->init.bak_memarea->init.name);
 	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
 	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
 	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
 	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (ma->init.bak_memarea)
+		fprintf(f, "  backup_alloc_fails: %" PRIu64 "\n", ma->bak_alloc_fails);
 	if (dump_all)
 		memarea_dump_all(ma, f);
 	memarea_unlock(ma);
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 4bf2f36c7c..815d0e3d75 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -40,6 +40,13 @@
  *   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.
+ * - It provides backup memory mechanism, the memarea could use another memarea
+ *   as a backup. It will attempts to allocate object from backup memarea when
+ *   the current memarea failed to allocate.
+ *   @note If the backup memarea is set improperly, loops may occur (e.g.
+ *   memarea-1's backup is memarea-2, and memarea-2's backup is memarea-1) and
+ *   the program will hangs, it is the responsibility of the application to
+ *   ensure that the loops do not form.
  */
 
 #include <stdbool.h>
@@ -106,6 +113,10 @@ struct rte_memarea_param {
 		 */
 		struct rte_memarea *user_memarea;
 	};
+	/** Backup memarea, which is used to handle the scenario where the
+	 * current memarea allocation failure.
+	 */
+	struct rte_memarea *bak_memarea;
 };
 
 /**
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v7 08/10] test/memarea: support backup memory test
  2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
                     ` (6 preceding siblings ...)
  2022-10-08 11:58   ` [PATCH v7 07/10] memarea: support backup memory mechanism Chengwen Feng
@ 2022-10-08 11:58   ` Chengwen Feng
  2022-10-08 11:58   ` [PATCH v7 09/10] memarea: detect memory corruption based on magic Chengwen Feng
                     ` (2 subsequent siblings)
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:58 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports backup memory mechanism test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index d159e4f2ef..28c66b886c 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -329,6 +329,46 @@ test_memarea_dump(void)
 	return 0;
 }
 
+static int
+test_memarea_backup(void)
+{
+	struct rte_memarea *ma, *bak_ma;
+	struct rte_memarea_param init;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	strcat(init.name, "_backup");
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	bak_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(bak_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE >> 2;
+	init.bak_memarea = bak_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for backup */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 3, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+	rte_memarea_free(ma, ptr);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(bak_ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -340,6 +380,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
+	MEMAREA_TEST_API_RUN(test_memarea_backup);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v7 09/10] memarea: detect memory corruption based on magic
  2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
                     ` (7 preceding siblings ...)
  2022-10-08 11:58   ` [PATCH v7 08/10] test/memarea: support backup memory test Chengwen Feng
@ 2022-10-08 11:58   ` Chengwen Feng
  2022-10-08 11:58   ` [PATCH v7 10/10] test/memarea: support no MT-safe test Chengwen Feng
  2022-10-08 12:14   ` [PATCH v7 00/10] introduce memarea library fengchengwen
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:58 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch provides lightweight mechanism for detecting memory
corruption which based on magic field in each element node.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 lib/memarea/memarea_private.h |  2 ++
 lib/memarea/rte_memarea.c     | 29 ++++++++++++++++++++++++++---
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 08735ca81f..4f5393e6f9 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -7,10 +7,12 @@
 
 #include <rte_memarea.h>
 
+#define MEMAREA_ELEM_MAGIC_NUM		0xbeef1234
 #define MEMAREA_FREE_ELEM_COOKIE	0xFFFFFFFF
 
 struct memarea_elem {
 	size_t   size;
+	uint32_t magic;
 	uint32_t cookie;
 	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
 	TAILQ_ENTRY(memarea_elem) elem_node;
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index f95f89b6ec..2983a36801 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -135,6 +135,7 @@ rte_memarea_create(const struct rte_memarea_param *init)
 	ma->top_addr = RTE_PTR_ADD(addr, init->total_sz - 1);
 	elem = addr;
 	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->magic = MEMAREA_ELEM_MAGIC_NUM;
 	elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
 	elem->refcnt = 0;
 	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
@@ -192,6 +193,7 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
 	new_elem = (struct memarea_elem *)RTE_PTR_ADD(elem, sizeof(struct memarea_elem) +
 							    align_size);
 	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->magic = MEMAREA_ELEM_MAGIC_NUM;
 	new_elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
 	new_elem->refcnt = 0;
 	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
@@ -219,6 +221,8 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 
 	memarea_lock(ma);
 	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (unlikely(elem->magic != MEMAREA_ELEM_MAGIC_NUM))
+			break;
 		if (elem->size < size)
 			continue;
 		if (memarea_whether_add_node(elem->size, size))
@@ -251,6 +255,7 @@ memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
 {
 	curr->size += next->size + sizeof(struct memarea_elem);
 	next->size = 0;
+	next->magic = ~MEMAREA_ELEM_MAGIC_NUM;
 	next->cookie = 0;
 	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
 	if (del_next_from_free)
@@ -293,6 +298,13 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		return;
 
 	memarea_lock(ma);
+	if (unlikely(elem->magic != MEMAREA_ELEM_MAGIC_NUM)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail!\n",
+			ma->init.name, elem->magic);
+		memarea_unlock(ma);
+		return;
+	}
+
 	if (unlikely(ptr < ma->area_addr || ptr > ma->top_addr)) {
 		rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
 		memarea_unlock(ma);
@@ -346,8 +358,11 @@ memarea_elem_list_num(struct rte_memarea *ma)
 	struct memarea_elem *elem;
 	uint32_t num = 0;
 
-	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM)
+			break;
 		num++;
+	}
 
 	return num;
 }
@@ -358,8 +373,11 @@ memarea_free_list_num(struct rte_memarea *ma)
 	struct memarea_elem *elem;
 	uint32_t num = 0;
 
-	TAILQ_FOREACH(elem, &ma->free_list, free_node)
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM)
+			break;
 		num++;
+	}
 
 	return num;
 }
@@ -370,9 +388,14 @@ memarea_dump_all(struct rte_memarea *ma, FILE *f)
 	struct memarea_elem *elem;
 
 	fprintf(f, "  regions:\n");
-	TAILQ_FOREACH(elem, &ma->elem_list, elem_node)
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_ELEM_MAGIC_NUM) {
+			fprintf(f, "    magic: 0x%x check fail!\n", elem->magic);
+			break;
+		}
 		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
 			elem->size, elem->cookie, elem->refcnt);
+	}
 }
 
 int
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v7 10/10] test/memarea: support no MT-safe test
  2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
                     ` (8 preceding siblings ...)
  2022-10-08 11:58   ` [PATCH v7 09/10] memarea: detect memory corruption based on magic Chengwen Feng
@ 2022-10-08 11:58   ` Chengwen Feng
  2022-10-08 12:14   ` [PATCH v7 00/10] introduce memarea library fengchengwen
  10 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-08 11:58 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

MT-safe is enabled by default in previous test, this patch adds no
MT-safe test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 28c66b886c..adadc6dcfc 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -369,6 +369,35 @@ test_memarea_backup(void)
 	return 0;
 }
 
+static int
+test_memarea_no_mt_safe(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_def_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init.mt_safe = false;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for all API */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	rte_memarea_free(ma, rte_memarea_alloc(ma, 1, 0));
+	rte_memarea_update_refcnt(ma, rte_memarea_alloc(ma, 1, 0), 1);
+	rte_memarea_update_refcnt(ma, rte_memarea_alloc(ma, 1, 0), -1);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -381,6 +410,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
 	MEMAREA_TEST_API_RUN(test_memarea_backup);
+	MEMAREA_TEST_API_RUN(test_memarea_no_mt_safe);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v7 00/10] introduce memarea library
  2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
                     ` (9 preceding siblings ...)
  2022-10-08 11:58   ` [PATCH v7 10/10] test/memarea: support no MT-safe test Chengwen Feng
@ 2022-10-08 12:14   ` fengchengwen
  10 siblings, 0 replies; 222+ messages in thread
From: fengchengwen @ 2022-10-08 12:14 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

On 2022/10/8 19:58, Chengwen Feng wrote:
> The memarea library is an allocator of variable-size object which based
> on a memory region. The main features are as follows:
> 

...

> 
> ---
> v6:
> * address Mattias's comments.
> * repost patches as there are spread over different series in patchwork.

Sorry about the confusion, the repost operation should be classified as v7.
As it's cover-letter, I didn't send v8 to fix it. If necessary, I will send v8.

> v5:
> * fix 09/10 patch spell warning.
> v4:
> * repost patches as there are spread over different series in patchwork.
> v3:
> * add memory source of RTE memory.
> * add algorithm field to facilitate the introduction of new algorithms.
> * fix memarea log don't output problem.
> v2:
> * fix compile issues reported by dpdk-test-report.
> * address Dimitry and Jerin's comments.
> * add no MT-safe test.
> 
>  MAINTAINERS                            |   6 +
>  app/test/meson.build                   |   2 +
>  app/test/test_memarea.c                | 420 ++++++++++++++++++++++++
>  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  |  56 ++++
>  doc/guides/rel_notes/release_22_11.rst |   6 +
>  lib/eal/common/eal_common_log.c        |   1 +
>  lib/eal/include/rte_log.h              |   1 +
>  lib/memarea/memarea_private.h          |  37 +++
>  lib/memarea/meson.build                |  16 +
>  lib/memarea/rte_memarea.c              | 428 +++++++++++++++++++++++++
>  lib/memarea/rte_memarea.h              | 234 ++++++++++++++
>  lib/memarea/version.map                |  16 +
>  lib/meson.build                        |   1 +
>  16 files changed, 1228 insertions(+), 1 deletion(-)
>  create mode 100644 app/test/test_memarea.c
>  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
> 

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v5 01/10] memarea: introduce memarea library
  2022-10-08  7:53       ` fengchengwen
@ 2022-10-10 16:53         ` Mattias Rönnblom
  2022-10-10 23:33           ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: Mattias Rönnblom @ 2022-10-10 16:53 UTC (permalink / raw)
  To: fengchengwen, datshan, david.marchand, mb, anatoly.burakov,
	dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev

On 2022-10-08 09:53, fengchengwen wrote:
> Hi Mattias, Thanks for your review, most will fix in v6.
> 
> On 2022/10/7 4:15, Mattias Rönnblom wrote:
>> On 2022-10-05 06:09, datshan wrote:
>>> From: Chengwen Feng <fengchengwen@huawei.com>
>>>
>>> The memarea library is an allocator of variable-size object which based
>>> on a memory region.
>>>
>>> This patch provides create/destroy API.
>>>
>>> Signed-off-by: Chengwen Feng <fengchengwen@huawei.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  |  39 ++++++
>>>    doc/guides/rel_notes/release_22_11.rst |   6 +
>>>    lib/eal/common/eal_common_log.c        |   1 +
>>>    lib/eal/include/rte_log.h              |   1 +
>>>    lib/memarea/memarea_private.h          |  30 +++++
>>>    lib/memarea/meson.build                |  16 +++
>>>    lib/memarea/rte_memarea.c              | 157 +++++++++++++++++++++++++
>>>    lib/memarea/rte_memarea.h              | 145 +++++++++++++++++++++++
>>>    lib/memarea/version.map                |  12 ++
>>>    lib/meson.build                        |   1 +
>>>    14 files changed, 417 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 a55b379d73..b9c638221d 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -1550,6 +1550,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>
>>> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
>>> index de488c7abf..24456604f8 100644
>>> --- a/doc/api/doxy-api-index.md
>>> +++ b/doc/api/doxy-api-index.md
>>> @@ -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),
>>> diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
>>> index f0886c3bd1..8334ebcbd6 100644
>>> --- a/doc/api/doxy-api.conf.in
>>> +++ b/doc/api/doxy-api.conf.in
>>> @@ -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 \
>>> diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
>>> index 8564883018..e9015d65e3 100644
>>> --- a/doc/guides/prog_guide/index.rst
>>> +++ b/doc/guides/prog_guide/index.rst
>>> @@ -37,6 +37,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..b96dad15f6
>>> --- /dev/null
>>> +++ b/doc/guides/prog_guide/memarea_lib.rst
>>> @@ -0,0 +1,39 @@
>>> +..  SPDX-License-Identifier: BSD-3-Clause
>>> +    Copyright(c) 2022 HiSilicon Limited
>>> +
>>> +Memarea Library
>>> +===============
>>> +
>>> +Introduction
>>> +------------
>>> +
>>> +The memarea library provides an allocator of variable-size objects, it is
>>> +oriented towards the application layer, which could provides 'region-based
>>> +memory management' function [1].
>>> +
>>> +The main features are as follows:
>>> +
>>> +* The default aligement size is ``RTE_CACHE_LINE_SIZE``.
>>> +
>>> +* The memory region can be initialized from the following memory sources:
>>> +  a) RTE memory: e.g. invoke ``rte_malloc_socket`` to obtain. b) System API:
>>> +  e.g. invoke posix_memalign to obtain. c) User provided address: it can be from
>>> +  extendedd memory as long as it is available. d) User provided memarea: it can
>>> +  be from another memarea.
>>> +
>>> +* It provides refcnt feature which could be useful in multi-reader scenario.
>>> +
>>> +* 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.
>>> +
>>> +Reference
>>> +---------
>>> +
>>> +[1] https://en.wikipedia.org/wiki/Region-based_memory_management
>>> diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
>>> index 5d8ef669b8..4c1f760b98 100644
>>> --- a/doc/guides/rel_notes/release_22_11.rst
>>> +++ b/doc/guides/rel_notes/release_22_11.rst
>>> @@ -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, which could provides 'region-based memory
>>> +  management' function.
>>> +
>>>    * **Added configuration for asynchronous flow connection tracking.**
>>>        Added connection tracking action number hint to ``rte_flow_configure``
>>> diff --git a/lib/eal/common/eal_common_log.c b/lib/eal/common/eal_common_log.c
>>> index bd7b188ceb..3d62af59c6 100644
>>> --- a/lib/eal/common/eal_common_log.c
>>> +++ b/lib/eal/common/eal_common_log.c
>>> @@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
>>>        {RTE_LOGTYPE_EFD,        "lib.efd"},
>>>        {RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
>>>        {RTE_LOGTYPE_GSO,        "lib.gso"},
>>> +    {RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
>>>        {RTE_LOGTYPE_USER1,      "user1"},
>>>        {RTE_LOGTYPE_USER2,      "user2"},
>>>        {RTE_LOGTYPE_USER3,      "user3"},
>>> diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
>>> index 25ce42cdfc..708f3a39dd 100644
>>> --- a/lib/eal/include/rte_log.h
>>> +++ b/lib/eal/include/rte_log.h
>>> @@ -48,6 +48,7 @@ extern "C" {
>>>    #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
>>>    #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
>>>    #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
>>> +#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
>>>      /* these log types can be used in an application */
>>>    #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
>>> diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
>>> new file mode 100644
>>> index 0000000000..c76392d3e6
>>> --- /dev/null
>>> +++ b/lib/memarea/memarea_private.h
>>> @@ -0,0 +1,30 @@
>>> +/* SPDX-License-Identifier: BSD-3-Clause
>>> + * Copyright(c) 2022 HiSilicon Limited
>>> + */
>>> +
>>> +#ifndef MEMAREA_PRIVATE_H
>>> +#define MEMAREA_PRIVATE_H
>>> +
>>> +#include <rte_memarea.h>
>>> +
>>> +#define MEMAREA_FREE_ELEM_COOKIE    0xFFFFFFFF
>>> +
>>> +struct memarea_elem {
>>> +    size_t   size;
>>> +    uint32_t cookie;
>>> +    int32_t  refcnt; /* Non-zero indicates that it has been allocated */
>>> +    TAILQ_ENTRY(memarea_elem) elem_node;
>>> +    TAILQ_ENTRY(memarea_elem) free_node;
>>> +} __rte_cache_aligned;
>>> +
>>
>> Why is the elem type cache line aligned? Need the elem data start be cache line aligned?
> 
> Yes, the elem data align at cache-line default.
> 
>>
>>> +TAILQ_HEAD(memarea_elem_list, memarea_elem);
>>> +
>>> +struct rte_memarea {
>>> +    struct rte_memarea_param init;
>>> +    rte_spinlock_t           lock;
>>> +    void                    *area_addr;
>>> +    struct memarea_elem_list elem_list;
>>> +    struct memarea_elem_list free_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..0a74fb4cd1
>>> --- /dev/null
>>> +++ b/lib/memarea/meson.build
>>> @@ -0,0 +1,16 @@
>>> +# SPDX-License-Identifier: BSD-3-Clause
>>> +# Copyright(c) 2022 HiSilicon Limited
>>> +
>>> +if is_windows
>>> +    build = false
>>> +    reason = 'not supported on Windows'
>>> +    subdir_done()
>>> +endif
>>> +
>>> +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..868da7661d
>>> --- /dev/null
>>> +++ b/lib/memarea/rte_memarea.c
>>> @@ -0,0 +1,157 @@
>>> +/* SPDX-License-Identifier: BSD-3-Clause
>>> + * Copyright(c) 2022 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"
>>> +
>>> +static int
>>> +memarea_check_param(const struct rte_memarea_param *init)
>>> +{
>>> +    size_t len;
>>> +
>>> +    if (init == NULL) {
>>> +        RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
>>> +    if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
>>> +        RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    if (init->source != RTE_MEMAREA_SOURCE_RTE_MEMORY &&
>>> +        init->source != RTE_MEMAREA_SOURCE_SYSTEM_API &&
>>> +        init->source != RTE_MEMAREA_SOURCE_USER_ADDR &&
>>> +        init->source != RTE_MEMAREA_SOURCE_USER_MEMAREA) {
>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
>>> +            init->name, init->source);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    if (init->total_sz <= sizeof(struct memarea_elem)) {
>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
>>> +            init->name, init->total_sz);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR && init->user_addr == NULL) {
>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr is NULL!\n", init->name);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR &&
>>> +        ((uintptr_t)init->user_addr & (RTE_CACHE_LINE_SIZE - 1))) {
>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr should align: %d!\n",
>>> +            init->name, RTE_CACHE_LINE_SIZE);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA && init->user_memarea == NULL) {
>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s user provided memarea is NULL!\n", init->name);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    if (init->alg != RTE_MEMAREA_ALG_DEFAULT) {
>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
>>> +            init->name, init->alg);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static void *
>>> +memarea_alloc_from_system_api(size_t size)
>>> +{
>>> +    void *ptr = NULL;
>>> +    int ret;
>>> +
>>> +    ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
>>> +    if (ret)
>>> +        return NULL;
>>> +    return ptr;
>>> +}
>>> +
>>> +static void *
>>> +memarea_alloc_area(const struct rte_memarea_param *init)
>>> +{
>>> +    void *ptr = NULL;
>>> +
>>> +    if (init->source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
>>
>> Delete MEMORY. Of course it's memory. What else? If you want to make it clear it's from the RTE heap, it should spell out HEAP. Or MALLOC.
> 
> HEAP seem better.
> 
>>
>>> +        ptr = rte_malloc_socket(NULL, init->total_sz, RTE_CACHE_LINE_SIZE,
>>> +                    init->numa_socket);
>>> +    else if (init->source == RTE_MEMAREA_SOURCE_SYSTEM_API)
>>> +        ptr = memarea_alloc_from_system_api(init->total_sz);
>>
>> "SYSTEM_API" doesn't strike me as a good name.
>>
>> RTE_MEMAREA_SOURCE_LIBC
> 
> LIBC seem better.
> 
>> RTE_MEMAREA_SOURCE_STD_HEAP
>> or at least remove API so it'll be
>> RTE_MEMAREA_SOURCE_SYSTEM
>>
>>> +    else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
>>
>> I would delete "ADDR".
> 
> +1
> 
>>
>>> +        ptr = init->user_addr;
>>> +
>>> +    if (ptr == NULL)
>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
>>> +
>>> +    return ptr;
>>> +}
>>> +
>>> +struct rte_memarea *
>>> +rte_memarea_create(const struct rte_memarea_param *init)
>>> +{
>>> +    struct memarea_elem *elem;
>>> +    struct rte_memarea *ma;
>>> +    void *addr;
>>> +    int ret;
>>> +
>>> +    ret = memarea_check_param(init);
>>> +    if (ret)
>>> +        return NULL;
>>> +
>>> +    addr = memarea_alloc_area(init);
>>> +    if (addr == NULL)
>>> +        return NULL;
>>> +
>>> +    ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
>>> +    if (ma == NULL) {
>>> +        RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
>>> +        return NULL;
>>> +    }
>>> +
>>> +    ma->init = *init;
>>> +    rte_spinlock_init(&ma->lock);
>>> +    TAILQ_INIT(&ma->elem_list);
>>> +    TAILQ_INIT(&ma->free_list);
>>> +    ma->area_addr = addr;
>>> +    elem = addr;
>>> +    elem->size = init->total_sz - sizeof(struct memarea_elem);
>>> +    elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
>>> +    elem->refcnt = 0;
>>> +    TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
>>> +    TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
>>> +
>>> +    return ma;
>>> +}
>>> +
>>> +static void
>>> +memarea_free_area(struct rte_memarea *ma)
>>> +{
>>> +    if (ma->init.source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
>>> +        rte_free(ma->area_addr);
>>> +    else if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
>>> +        free(ma->area_addr);
>>> +}
>>> +
>>> +void
>>> +rte_memarea_destroy(struct rte_memarea *ma)
>>> +{
>>> +    if (ma == NULL)
>>> +        return;
>>> +    memarea_free_area(ma);
>>> +    rte_free(ma);
>>> +}
>>> diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
>>> new file mode 100644
>>> index 0000000000..543cda4cac
>>> --- /dev/null
>>> +++ b/lib/memarea/rte_memarea.h
>>> @@ -0,0 +1,145 @@
>>> +/* SPDX-License-Identifier: BSD-3-Clause
>>> + * Copyright(c) 2022 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 default alignment size is RTE_CACHE_LINE_SIZE.
>>
>> This can be read as two things: the object size is aligned, or the start address is aligned.
> 
> It means the start address align, will define more clear in v6.
> 
>>
>>> + * - The memory region can be initialized from the following memory sources:
>>> + *   1. RTE memory: e.g. invoke rte_malloc_socket to obtain.
>>
>> Remove "to obtain", or add "memory" after "obtain". Do you really mean "e.g.", and not "i.e."?
> 
> will fix in v6.
> 
>>
>>> + *   2. System API: e.g. invoke posix_memalign to obtain.
>>> + *   3. User provided address: it can be from extended memory as long as it is
>>> + *      available. The address must be aligned to RTE_CACHE_LINE_SIZE.
>>
>> What is extended memory?
> 
> Like rte_extmen
> 
>>
>>> + *   4) User provided memarea: it can be from another memarea. So we can build
>>> + *      the following memory management structure:
>>> + *         \code{.unparsed}
>>> + *                           -------------
>>> + *                           | memarea-1 |
>>> + *                           -------------
>>> + *                                 |
>>> + *                                 v
>>> + *                  -------------------------------
>>> + *                  |               |             |
>>> + *                  v               v             v
>>> + *            -------------   -------------   ----------
>>> + *            | memarea-2 |   | memarea-3 |   | object |
>>> + *            -------------   -------------   ----------
>>> + *         \endcode
>>> + *      As shown above, the memarea-2/3 both create from memarea-1's memory.
>>> + * - It provides refcnt feature which could be useful in multi-reader scenario.
>>> + * - 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_RTE_MEMORY,
>>> +    /** Memory source comes from system API. */
>>> +    RTE_MEMAREA_SOURCE_SYSTEM_API,
>>> +    /** Memory source comes from user-provided address. */
>>> +    RTE_MEMAREA_SOURCE_USER_ADDR,
>>> +    /** Memory source comes from user-provided memarea. */
>>> +    RTE_MEMAREA_SOURCE_USER_MEMAREA,
>>> +};
>>> +
>>> +/**
>>> + * Memarea memory management algorithm.
>>> + */
>>> +enum rte_memarea_alg {
>>> +    /* The default management algorithm is a variant of the next fit
>>> +     * algorithm. It uses a free-list to apply for memory and uses an
>>> +     * element-list in ascending order of address to support merging
>>> +     * upon free.
>>> +     */
>>> +    RTE_MEMAREA_ALG_DEFAULT,
>>> +};

Do you need to expose the algorithm/management scheme option in the 
public API, if there is only one implementation to choose from?

You can always have a rte_memarea_create_alg(/../) in the future, or 
just change the signature between ABI-breaking releases.

Also, shouldn't the default algorithm have name? Rather than just 
DEFAULT. RTE_MEMAREA_ALG_NEXTFIT maybe.

>>> +
>>> +struct rte_memarea;
>>> +
>>> +struct rte_memarea_param {
>>
>> Does this struct need to be public?
> 
> Yes, the rte_memarea_param contains create parameters for create memarea which need be public.
> 

Why isn't the public API just a bunch of create-function parameters? Or, 
alternatively, you have an assortment of create functions, for different 
combinations of parameters. Then you don't have to think about having 
reserved bits or other kind of ABI-related issues due to the struct 
being public.

> And rte_memarea which pointer implementation struction which just a declare here.
> 
>>
>>> +    char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
>>> +    enum rte_memarea_source source;  /**< Memory source of memarea. */
>>> +    enum rte_memarea_alg 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;
>>
>> Why not bool?
> 
> Use bit field other than bool will provides more reserved field.
> 
>>
>>> +    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_RTE_MEMORY.
>>> +         */
>>> +        int numa_socket;
>>> +        /** User provided address, this field is valid only when the
>>> +         * source is set to be RTE_MEMAREA_SOURCE_USER_ADDR.
>>> +         * Note: the provided address must align at least
>>> +         * RTE_CACHE_LINE_SIZE.
>>> +         */
>>> +        void *user_addr;
>>> +        /** User provided memarea, this field is valid only when the
>>> +         * source is set to be RTE_MEMAREA_SOURCE_USER_MEMAREA.
>>> +         */
>>> +        struct rte_memarea *user_memarea;
>>> +    };
>>> +};
>>> +
>>> +/**
>>> + * @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 */
>>> 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 c648f7d800..521a25d6c0 100644
>>> --- a/lib/meson.build
>>> +++ b/lib/meson.build
>>> @@ -42,6 +42,7 @@ libraries = [
>>>            'kni',
>>>            'latencystats',
>>>            'lpm',
>>> +        'memarea',
>>>            'member',
>>>            'pcapng',
>>>            'power',
>>
>> .

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v5 01/10] memarea: introduce memarea library
  2022-10-10 16:53         ` Mattias Rönnblom
@ 2022-10-10 23:33           ` fengchengwen
  2022-10-11 15:35             ` Mattias Rönnblom
  0 siblings, 1 reply; 222+ messages in thread
From: fengchengwen @ 2022-10-10 23:33 UTC (permalink / raw)
  To: Mattias Rönnblom, datshan, david.marchand, mb,
	anatoly.burakov, dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev

On 2022/10/11 0:53, Mattias Rönnblom wrote:
> On 2022-10-08 09:53, fengchengwen wrote:
>> Hi Mattias, Thanks for your review, most will fix in v6.
>>
>> On 2022/10/7 4:15, Mattias Rönnblom wrote:
>>> On 2022-10-05 06:09, datshan wrote:
>>>> From: Chengwen Feng <fengchengwen@huawei.com>
>>>>
>>>> The memarea library is an allocator of variable-size object which 
>>>> based
>>>> on a memory region.
>>>>
>>>> This patch provides create/destroy API.
>>>>
>>>> Signed-off-by: Chengwen Feng <fengchengwen@huawei.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  |  39 ++++++
>>>>    doc/guides/rel_notes/release_22_11.rst |   6 +
>>>>    lib/eal/common/eal_common_log.c        |   1 +
>>>>    lib/eal/include/rte_log.h              |   1 +
>>>>    lib/memarea/memarea_private.h          |  30 +++++
>>>>    lib/memarea/meson.build                |  16 +++
>>>>    lib/memarea/rte_memarea.c              | 157 
>>>> +++++++++++++++++++++++++
>>>>    lib/memarea/rte_memarea.h              | 145 
>>>> +++++++++++++++++++++++
>>>>    lib/memarea/version.map                |  12 ++
>>>>    lib/meson.build                        |   1 +
>>>>    14 files changed, 417 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 a55b379d73..b9c638221d 100644
>>>> --- a/MAINTAINERS
>>>> +++ b/MAINTAINERS
>>>> @@ -1550,6 +1550,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>
>>>> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
>>>> index de488c7abf..24456604f8 100644
>>>> --- a/doc/api/doxy-api-index.md
>>>> +++ b/doc/api/doxy-api-index.md
>>>> @@ -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),
>>>> diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
>>>> index f0886c3bd1..8334ebcbd6 100644
>>>> --- a/doc/api/doxy-api.conf.in
>>>> +++ b/doc/api/doxy-api.conf.in
>>>> @@ -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 \
>>>> diff --git a/doc/guides/prog_guide/index.rst 
>>>> b/doc/guides/prog_guide/index.rst
>>>> index 8564883018..e9015d65e3 100644
>>>> --- a/doc/guides/prog_guide/index.rst
>>>> +++ b/doc/guides/prog_guide/index.rst
>>>> @@ -37,6 +37,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..b96dad15f6
>>>> --- /dev/null
>>>> +++ b/doc/guides/prog_guide/memarea_lib.rst
>>>> @@ -0,0 +1,39 @@
>>>> +..  SPDX-License-Identifier: BSD-3-Clause
>>>> +    Copyright(c) 2022 HiSilicon Limited
>>>> +
>>>> +Memarea Library
>>>> +===============
>>>> +
>>>> +Introduction
>>>> +------------
>>>> +
>>>> +The memarea library provides an allocator of variable-size 
>>>> objects, it is
>>>> +oriented towards the application layer, which could provides 
>>>> 'region-based
>>>> +memory management' function [1].
>>>> +
>>>> +The main features are as follows:
>>>> +
>>>> +* The default aligement size is ``RTE_CACHE_LINE_SIZE``.
>>>> +
>>>> +* The memory region can be initialized from the following memory 
>>>> sources:
>>>> +  a) RTE memory: e.g. invoke ``rte_malloc_socket`` to obtain. b) 
>>>> System API:
>>>> +  e.g. invoke posix_memalign to obtain. c) User provided address: 
>>>> it can be from
>>>> +  extendedd memory as long as it is available. d) User provided 
>>>> memarea: it can
>>>> +  be from another memarea.
>>>> +
>>>> +* It provides refcnt feature which could be useful in multi-reader 
>>>> scenario.
>>>> +
>>>> +* 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.
>>>> +
>>>> +Reference
>>>> +---------
>>>> +
>>>> +[1] https://en.wikipedia.org/wiki/Region-based_memory_management
>>>> diff --git a/doc/guides/rel_notes/release_22_11.rst 
>>>> b/doc/guides/rel_notes/release_22_11.rst
>>>> index 5d8ef669b8..4c1f760b98 100644
>>>> --- a/doc/guides/rel_notes/release_22_11.rst
>>>> +++ b/doc/guides/rel_notes/release_22_11.rst
>>>> @@ -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, which could provides 
>>>> 'region-based memory
>>>> +  management' function.
>>>> +
>>>>    * **Added configuration for asynchronous flow connection 
>>>> tracking.**
>>>>        Added connection tracking action number hint to 
>>>> ``rte_flow_configure``
>>>> diff --git a/lib/eal/common/eal_common_log.c 
>>>> b/lib/eal/common/eal_common_log.c
>>>> index bd7b188ceb..3d62af59c6 100644
>>>> --- a/lib/eal/common/eal_common_log.c
>>>> +++ b/lib/eal/common/eal_common_log.c
>>>> @@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
>>>>        {RTE_LOGTYPE_EFD,        "lib.efd"},
>>>>        {RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
>>>>        {RTE_LOGTYPE_GSO,        "lib.gso"},
>>>> +    {RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
>>>>        {RTE_LOGTYPE_USER1,      "user1"},
>>>>        {RTE_LOGTYPE_USER2,      "user2"},
>>>>        {RTE_LOGTYPE_USER3,      "user3"},
>>>> diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
>>>> index 25ce42cdfc..708f3a39dd 100644
>>>> --- a/lib/eal/include/rte_log.h
>>>> +++ b/lib/eal/include/rte_log.h
>>>> @@ -48,6 +48,7 @@ extern "C" {
>>>>    #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
>>>>    #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
>>>>    #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
>>>> +#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
>>>>      /* these log types can be used in an application */
>>>>    #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
>>>> diff --git a/lib/memarea/memarea_private.h 
>>>> b/lib/memarea/memarea_private.h
>>>> new file mode 100644
>>>> index 0000000000..c76392d3e6
>>>> --- /dev/null
>>>> +++ b/lib/memarea/memarea_private.h
>>>> @@ -0,0 +1,30 @@
>>>> +/* SPDX-License-Identifier: BSD-3-Clause
>>>> + * Copyright(c) 2022 HiSilicon Limited
>>>> + */
>>>> +
>>>> +#ifndef MEMAREA_PRIVATE_H
>>>> +#define MEMAREA_PRIVATE_H
>>>> +
>>>> +#include <rte_memarea.h>
>>>> +
>>>> +#define MEMAREA_FREE_ELEM_COOKIE    0xFFFFFFFF
>>>> +
>>>> +struct memarea_elem {
>>>> +    size_t   size;
>>>> +    uint32_t cookie;
>>>> +    int32_t  refcnt; /* Non-zero indicates that it has been 
>>>> allocated */
>>>> +    TAILQ_ENTRY(memarea_elem) elem_node;
>>>> +    TAILQ_ENTRY(memarea_elem) free_node;
>>>> +} __rte_cache_aligned;
>>>> +
>>>
>>> Why is the elem type cache line aligned? Need the elem data start be 
>>> cache line aligned?
>>
>> Yes, the elem data align at cache-line default.
>>
>>>
>>>> +TAILQ_HEAD(memarea_elem_list, memarea_elem);
>>>> +
>>>> +struct rte_memarea {
>>>> +    struct rte_memarea_param init;
>>>> +    rte_spinlock_t           lock;
>>>> +    void                    *area_addr;
>>>> +    struct memarea_elem_list elem_list;
>>>> +    struct memarea_elem_list free_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..0a74fb4cd1
>>>> --- /dev/null
>>>> +++ b/lib/memarea/meson.build
>>>> @@ -0,0 +1,16 @@
>>>> +# SPDX-License-Identifier: BSD-3-Clause
>>>> +# Copyright(c) 2022 HiSilicon Limited
>>>> +
>>>> +if is_windows
>>>> +    build = false
>>>> +    reason = 'not supported on Windows'
>>>> +    subdir_done()
>>>> +endif
>>>> +
>>>> +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..868da7661d
>>>> --- /dev/null
>>>> +++ b/lib/memarea/rte_memarea.c
>>>> @@ -0,0 +1,157 @@
>>>> +/* SPDX-License-Identifier: BSD-3-Clause
>>>> + * Copyright(c) 2022 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"
>>>> +
>>>> +static int
>>>> +memarea_check_param(const struct rte_memarea_param *init)
>>>> +{
>>>> +    size_t len;
>>>> +
>>>> +    if (init == NULL) {
>>>> +        RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
>>>> +    if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
>>>> +        RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", 
>>>> len);
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    if (init->source != RTE_MEMAREA_SOURCE_RTE_MEMORY &&
>>>> +        init->source != RTE_MEMAREA_SOURCE_SYSTEM_API &&
>>>> +        init->source != RTE_MEMAREA_SOURCE_USER_ADDR &&
>>>> +        init->source != RTE_MEMAREA_SOURCE_USER_MEMAREA) {
>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not 
>>>> supported!\n",
>>>> +            init->name, init->source);
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    if (init->total_sz <= sizeof(struct memarea_elem)) {
>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too 
>>>> small!\n",
>>>> +            init->name, init->total_sz);
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR && 
>>>> init->user_addr == NULL) {
>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr is 
>>>> NULL!\n", init->name);
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR &&
>>>> +        ((uintptr_t)init->user_addr & (RTE_CACHE_LINE_SIZE - 1))) {
>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr 
>>>> should align: %d!\n",
>>>> +            init->name, RTE_CACHE_LINE_SIZE);
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA && 
>>>> init->user_memarea == NULL) {
>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s user provided memarea 
>>>> is NULL!\n", init->name);
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    if (init->alg != RTE_MEMAREA_ALG_DEFAULT) {
>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
>>>> +            init->name, init->alg);
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static void *
>>>> +memarea_alloc_from_system_api(size_t size)
>>>> +{
>>>> +    void *ptr = NULL;
>>>> +    int ret;
>>>> +
>>>> +    ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
>>>> +    if (ret)
>>>> +        return NULL;
>>>> +    return ptr;
>>>> +}
>>>> +
>>>> +static void *
>>>> +memarea_alloc_area(const struct rte_memarea_param *init)
>>>> +{
>>>> +    void *ptr = NULL;
>>>> +
>>>> +    if (init->source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
>>>
>>> Delete MEMORY. Of course it's memory. What else? If you want to make 
>>> it clear it's from the RTE heap, it should spell out HEAP. Or MALLOC.
>>
>> HEAP seem better.
>>
>>>
>>>> +        ptr = rte_malloc_socket(NULL, init->total_sz, 
>>>> RTE_CACHE_LINE_SIZE,
>>>> +                    init->numa_socket);
>>>> +    else if (init->source == RTE_MEMAREA_SOURCE_SYSTEM_API)
>>>> +        ptr = memarea_alloc_from_system_api(init->total_sz);
>>>
>>> "SYSTEM_API" doesn't strike me as a good name.
>>>
>>> RTE_MEMAREA_SOURCE_LIBC
>>
>> LIBC seem better.
>>
>>> RTE_MEMAREA_SOURCE_STD_HEAP
>>> or at least remove API so it'll be
>>> RTE_MEMAREA_SOURCE_SYSTEM
>>>
>>>> +    else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
>>>
>>> I would delete "ADDR".
>>
>> +1
>>
>>>
>>>> +        ptr = init->user_addr;
>>>> +
>>>> +    if (ptr == NULL)
>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area 
>>>> fail!\n", init->name);
>>>> +
>>>> +    return ptr;
>>>> +}
>>>> +
>>>> +struct rte_memarea *
>>>> +rte_memarea_create(const struct rte_memarea_param *init)
>>>> +{
>>>> +    struct memarea_elem *elem;
>>>> +    struct rte_memarea *ma;
>>>> +    void *addr;
>>>> +    int ret;
>>>> +
>>>> +    ret = memarea_check_param(init);
>>>> +    if (ret)
>>>> +        return NULL;
>>>> +
>>>> +    addr = memarea_alloc_area(init);
>>>> +    if (addr == NULL)
>>>> +        return NULL;
>>>> +
>>>> +    ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), 
>>>> RTE_CACHE_LINE_SIZE);
>>>> +    if (ma == NULL) {
>>>> +        RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj 
>>>> fail!\n", init->name);
>>>> +        return NULL;
>>>> +    }
>>>> +
>>>> +    ma->init = *init;
>>>> +    rte_spinlock_init(&ma->lock);
>>>> +    TAILQ_INIT(&ma->elem_list);
>>>> +    TAILQ_INIT(&ma->free_list);
>>>> +    ma->area_addr = addr;
>>>> +    elem = addr;
>>>> +    elem->size = init->total_sz - sizeof(struct memarea_elem);
>>>> +    elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
>>>> +    elem->refcnt = 0;
>>>> +    TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
>>>> +    TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
>>>> +
>>>> +    return ma;
>>>> +}
>>>> +
>>>> +static void
>>>> +memarea_free_area(struct rte_memarea *ma)
>>>> +{
>>>> +    if (ma->init.source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
>>>> +        rte_free(ma->area_addr);
>>>> +    else if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
>>>> +        free(ma->area_addr);
>>>> +}
>>>> +
>>>> +void
>>>> +rte_memarea_destroy(struct rte_memarea *ma)
>>>> +{
>>>> +    if (ma == NULL)
>>>> +        return;
>>>> +    memarea_free_area(ma);
>>>> +    rte_free(ma);
>>>> +}
>>>> diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
>>>> new file mode 100644
>>>> index 0000000000..543cda4cac
>>>> --- /dev/null
>>>> +++ b/lib/memarea/rte_memarea.h
>>>> @@ -0,0 +1,145 @@
>>>> +/* SPDX-License-Identifier: BSD-3-Clause
>>>> + * Copyright(c) 2022 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 default alignment size is RTE_CACHE_LINE_SIZE.
>>>
>>> This can be read as two things: the object size is aligned, or the 
>>> start address is aligned.
>>
>> It means the start address align, will define more clear in v6.
>>
>>>
>>>> + * - The memory region can be initialized from the following 
>>>> memory sources:
>>>> + *   1. RTE memory: e.g. invoke rte_malloc_socket to obtain.
>>>
>>> Remove "to obtain", or add "memory" after "obtain". Do you really 
>>> mean "e.g.", and not "i.e."?
>>
>> will fix in v6.
>>
>>>
>>>> + *   2. System API: e.g. invoke posix_memalign to obtain.
>>>> + *   3. User provided address: it can be from extended memory as 
>>>> long as it is
>>>> + *      available. The address must be aligned to 
>>>> RTE_CACHE_LINE_SIZE.
>>>
>>> What is extended memory?
>>
>> Like rte_extmen
>>
>>>
>>>> + *   4) User provided memarea: it can be from another memarea. So 
>>>> we can build
>>>> + *      the following memory management structure:
>>>> + *         \code{.unparsed}
>>>> + *                           -------------
>>>> + *                           | memarea-1 |
>>>> + *                           -------------
>>>> + *                                 |
>>>> + *                                 v
>>>> + *                  -------------------------------
>>>> + *                  |               |             |
>>>> + *                  v               v             v
>>>> + *            -------------   -------------   ----------
>>>> + *            | memarea-2 |   | memarea-3 |   | object |
>>>> + *            -------------   -------------   ----------
>>>> + *         \endcode
>>>> + *      As shown above, the memarea-2/3 both create from 
>>>> memarea-1's memory.
>>>> + * - It provides refcnt feature which could be useful in 
>>>> multi-reader scenario.
>>>> + * - 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_RTE_MEMORY,
>>>> +    /** Memory source comes from system API. */
>>>> +    RTE_MEMAREA_SOURCE_SYSTEM_API,
>>>> +    /** Memory source comes from user-provided address. */
>>>> +    RTE_MEMAREA_SOURCE_USER_ADDR,
>>>> +    /** Memory source comes from user-provided memarea. */
>>>> +    RTE_MEMAREA_SOURCE_USER_MEMAREA,
>>>> +};
>>>> +
>>>> +/**
>>>> + * Memarea memory management algorithm.
>>>> + */
>>>> +enum rte_memarea_alg {
>>>> +    /* The default management algorithm is a variant of the next fit
>>>> +     * algorithm. It uses a free-list to apply for memory and uses an
>>>> +     * element-list in ascending order of address to support merging
>>>> +     * upon free.
>>>> +     */
>>>> +    RTE_MEMAREA_ALG_DEFAULT,
>>>> +};
>
> Do you need to expose the algorithm/management scheme option in the 
> public API, if there is only one implementation to choose from?


Yes, we plan to support SLAB algorithm in future by adding a new alg.


>
> You can always have a rte_memarea_create_alg(/../) in the future, or 
> just change the signature between ABI-breaking releases.


I don't think add a new API is a good idea.

You can see that, we put all init parameter in one struct in this 
design, and could extend function by adding new field .


>
>
> Also, shouldn't the default algorithm have name? Rather than just 
> DEFAULT. RTE_MEMAREA_ALG_NEXTFIT maybe.


Yes, will fix in next version.


>
>>>> +
>>>> +struct rte_memarea;
>>>> +
>>>> +struct rte_memarea_param {
>>>
>>> Does this struct need to be public?
>>
>> Yes, the rte_memarea_param contains create parameters for create 
>> memarea which need be public.
>>
>
> Why isn't the public API just a bunch of create-function parameters? 
> Or, alternatively, you have an assortment of create functions, for 
> different combinations of parameters. Then you don't have to think 
> about having reserved bits or other kind of ABI-related issues due to 
> the struct being public.


These are two API design styles,and I prefer the one create-function API 
style just it seem simple (by inner usage voice).


>
>> And rte_memarea which pointer implementation struction which just a 
>> declare here.
>>
>>>
>>>> +    char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
>>>> +    enum rte_memarea_source source;  /**< Memory source of 
>>>> memarea. */
>>>> +    enum rte_memarea_alg 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;
>>>
>>> Why not bool?
>>
>> Use bit field other than bool will provides more reserved field.
>>
>>>
>>>> +    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_RTE_MEMORY.
>>>> +         */
>>>> +        int numa_socket;
>>>> +        /** User provided address, this field is valid only when the
>>>> +         * source is set to be RTE_MEMAREA_SOURCE_USER_ADDR.
>>>> +         * Note: the provided address must align at least
>>>> +         * RTE_CACHE_LINE_SIZE.
>>>> +         */
>>>> +        void *user_addr;
>>>> +        /** User provided memarea, this field is valid only when the
>>>> +         * source is set to be RTE_MEMAREA_SOURCE_USER_MEMAREA.
>>>> +         */
>>>> +        struct rte_memarea *user_memarea;
>>>> +    };
>>>> +};
>>>> +
>>>> +/**
>>>> + * @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 */
>>>> 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 c648f7d800..521a25d6c0 100644
>>>> --- a/lib/meson.build
>>>> +++ b/lib/meson.build
>>>> @@ -42,6 +42,7 @@ libraries = [
>>>>            'kni',
>>>>            'latencystats',
>>>>            'lpm',
>>>> +        'memarea',
>>>>            'member',
>>>>            'pcapng',
>>>>            'power',
>>>
>>> .
>

^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v8 0/9] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (11 preceding siblings ...)
  2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
@ 2022-10-11 12:17 ` Chengwen Feng
  2022-10-11 12:17   ` [PATCH v8 1/9] memarea: " Chengwen Feng
                     ` (8 more replies)
  2022-10-12 10:47 ` [PATCH v9 0/7] introduce memarea library Chengwen Feng
                   ` (10 subsequent siblings)
  23 siblings, 9 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-11 12:17 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- The allocated object aligned at RTE_CACHE_LINE_SIZE default.

- 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. User memory: it can be from e.g. rte_extmem_xxx as long as it is
     available. The memory's start address must be aligned to
     RTE_CACHE_LINE_SIZE.
  4. Another memarea: it can be from another memarea.

- It provides refcnt feature which could be useful in multi-reader
  scenario.

- It provides backup memory mechanism, the memarea could use another
  memarea as a backup. It will attempts to allocate object from backup
  memarea when the current memarea failed to allocate.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>

Chengwen Feng (9):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc/free/update-refcnt API
  test/memarea: support alloc/free/update-refcnt test
  memarea: support dump API
  test/memarea: support dump test
  memarea: support backup memory mechanism
  test/memarea: support backup memory test
  app/test: add memarea to malloc-perf-autotest

---
v8:
* address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
* small feature patches are combined.
* enhanced backup memory mechanism.
* add memarea to malloc-perf-autotest.
* other tiny naming optimize.
v7:
* repost patches as there are spread over different series in patchwork.
v6:
* address Mattias's comments.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_malloc_perf.c            |  55 ++-
 app/test/test_memarea.c                | 442 +++++++++++++++++++++++
 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  |  56 +++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  39 ++
 lib/memarea/meson.build                |  16 +
 lib/memarea/rte_memarea.c              | 476 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 223 ++++++++++++
 lib/memarea/version.map                |  16 +
 lib/meson.build                        |   1 +
 17 files changed, 1343 insertions(+), 2 deletions(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v8 1/9] memarea: introduce memarea library
  2022-10-11 12:17 ` [PATCH v8 0/9] " Chengwen Feng
@ 2022-10-11 12:17   ` Chengwen Feng
  2022-10-11 15:58     ` Dmitry Kozlyuk
  2022-10-11 12:17   ` [PATCH v8 2/9] test/memarea: support memarea test Chengwen Feng
                     ` (7 subsequent siblings)
  8 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2022-10-11 12:17 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

The memarea library is an allocator of variable-size object which based
on a memory region.

This patch provides create/destroy API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.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  |  39 ++++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  33 ++++++
 lib/memarea/meson.build                |  16 +++
 lib/memarea/rte_memarea.c              | 158 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 136 +++++++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 14 files changed, 412 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 2bd4a55f1b..3d8d4c2dbe 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1551,6 +1551,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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index de488c7abf..24456604f8 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -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),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index f0886c3bd1..8334ebcbd6 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -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 \
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..85ad57145f
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -0,0 +1,39 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2022 HiSilicon Limited
+
+Memarea Library
+===============
+
+Introduction
+------------
+
+The memarea library provides an allocator of variable-size objects, it is
+oriented towards the application layer, which could provides 'region-based
+memory management' function [1].
+
+The main features are as follows:
+
+* The allocated object aligned at ``RTE_CACHE_LINE_SIZE`` default.
+
+* The memory region can be initialized from the following memory sources:
+  a) HEAP: e.g. invoke ``rte_malloc_socket``. b) LIBC: e.g. invoke
+  posix_memalign to obtain. c) User memory: it can be from e.g. rte_extmem_xxx
+  as long as it is available. d) Another memarea: it can be allocated from
+  another memarea.
+
+* It provides refcnt feature which could be useful in multi-reader scenario.
+
+* 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.
+
+Reference
+---------
+
+[1] https://en.wikipedia.org/wiki/Region-based_memory_management
diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
index 2da8bc9661..f5a67cec7b 100644
--- a/doc/guides/rel_notes/release_22_11.rst
+++ b/doc/guides/rel_notes/release_22_11.rst
@@ -63,6 +63,12 @@ New Features
   In theory this implementation should work with any target based on
   ``LoongArch`` ISA.
 
+* **Added memarea library.**
+
+  The memarea library is an allocator of variable-size objects, it is oriented
+  towards the application layer, which could provides 'region-based memory
+  management' function.
+
 * **Added support for multiple mbuf pools per ethdev Rx queue.**
 
   The capability allows application to provide many mempools
diff --git a/lib/eal/common/eal_common_log.c b/lib/eal/common/eal_common_log.c
index bd7b188ceb..3d62af59c6 100644
--- a/lib/eal/common/eal_common_log.c
+++ b/lib/eal/common/eal_common_log.c
@@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
 	{RTE_LOGTYPE_EFD,        "lib.efd"},
 	{RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
 	{RTE_LOGTYPE_GSO,        "lib.gso"},
+	{RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
 	{RTE_LOGTYPE_USER1,      "user1"},
 	{RTE_LOGTYPE_USER2,      "user2"},
 	{RTE_LOGTYPE_USER3,      "user3"},
diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
index 25ce42cdfc..708f3a39dd 100644
--- a/lib/eal/include/rte_log.h
+++ b/lib/eal/include/rte_log.h
@@ -48,6 +48,7 @@ extern "C" {
 #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
 #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
 #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
+#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..59be9c1d00
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef MEMAREA_PRIVATE_H
+#define MEMAREA_PRIVATE_H
+
+#include <rte_memarea.h>
+
+#define MEMAREA_AVAILABLE_ELEM_MAGIC	0xbeef1234
+#define MEMAREA_ALLOCATED_ELEM_MAGIC	0xbeef1230
+#define MEMAREA_AVAILABLE_ELEM_COOKIE	0xffffffff
+
+struct memarea_elem {
+	TAILQ_ENTRY(memarea_elem) elem_node;
+	TAILQ_ENTRY(memarea_elem) free_node;
+	size_t   size;
+	uint32_t magic;
+	uint32_t cookie;
+	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
+} __rte_cache_aligned;
+
+TAILQ_HEAD(memarea_elem_list, memarea_elem);
+
+struct rte_memarea {
+	struct rte_memarea_param init;
+	rte_spinlock_t           lock;
+	void                    *area_addr;
+	struct memarea_elem_list elem_list;
+	struct memarea_elem_list free_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..0a74fb4cd1
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2022 HiSilicon Limited
+
+if is_windows
+    build = false
+    reason = 'not supported on Windows'
+    subdir_done()
+endif
+
+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..85975029e2
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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"
+
+static int
+memarea_check_param(const struct rte_memarea_param *init)
+{
+	size_t len;
+
+	if (init == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
+		return -EINVAL;
+	}
+
+	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
+	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
+		return -EINVAL;
+	}
+
+	if (init->source != RTE_MEMAREA_SOURCE_HEAP &&
+	    init->source != RTE_MEMAREA_SOURCE_LIBC &&
+	    init->source != RTE_MEMAREA_SOURCE_USER &&
+	    init->source != RTE_MEMAREA_SOURCE_MEMAREA) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
+			init->name, init->source);
+		return -EINVAL;
+	}
+
+	if (init->total_sz <= sizeof(struct memarea_elem)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
+			init->name, init->total_sz);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER && init->user_addr == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user memory address is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_USER &&
+	    ((uintptr_t)init->user_addr & (RTE_CACHE_LINE_SIZE - 1))) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s user memory address should align: %d!\n",
+			init->name, RTE_CACHE_LINE_SIZE);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_MEMAREA && init->src_memarea == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source memarea is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->alg != RTE_MEMAREA_ALG_NEXTFIT) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
+			init->name, init->alg);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+memarea_alloc_from_libc(size_t size)
+{
+	void *ptr = NULL;
+	int ret;
+
+	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+	if (ret)
+		return NULL;
+	return ptr;
+}
+
+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);
+	else if (init->source == RTE_MEMAREA_SOURCE_USER)
+		ptr = init->user_addr;
+
+	if (ptr == NULL)
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
+
+	return ptr;
+}
+
+struct rte_memarea *
+rte_memarea_create(const struct rte_memarea_param *init)
+{
+	struct memarea_elem *elem;
+	struct rte_memarea *ma;
+	void *addr;
+	int ret;
+
+	ret = memarea_check_param(init);
+	if (ret)
+		return NULL;
+
+	addr = memarea_alloc_area(init);
+	if (addr == NULL)
+		return NULL;
+
+	ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
+	if (ma == NULL) {
+		RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
+		return NULL;
+	}
+
+	ma->init = *init;
+	rte_spinlock_init(&ma->lock);
+	TAILQ_INIT(&ma->elem_list);
+	TAILQ_INIT(&ma->free_list);
+	ma->area_addr = addr;
+	elem = addr;
+	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
+	TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+	elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+	elem->refcnt = 0;
+
+	return ma;
+}
+
+static void
+memarea_free_area(struct rte_memarea *ma)
+{
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		rte_free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
+		free(ma->area_addr);
+}
+
+void
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+	if (ma == NULL)
+		return;
+	memarea_free_area(ma);
+	rte_free(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
new file mode 100644
index 0000000000..ac2d63a4d3
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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 allocated object aligned at RTE_CACHE_LINE_SIZE default.
+ *
+ * - 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. User memory: it can be from e.g. rte_extmem_xxx as long as it is
+ *      available. The memory's start address must be aligned to
+ *      RTE_CACHE_LINE_SIZE.
+ *   4. Another memarea: it can be allocated from another memarea.
+ *
+ * - It provides refcnt feature which could be useful in multi-reader scenario.
+ *
+ * - 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 user memory. */
+	RTE_MEMAREA_SOURCE_USER,
+	/** Memory source comes from another memarea. */
+	RTE_MEMAREA_SOURCE_MEMAREA,
+};
+
+/**
+ * Memarea memory management algorithm.
+ */
+enum rte_memarea_alg {
+	/** The default management algorithm is a variant of the next fit
+	 * algorithm. It uses a free-list to apply for memory and uses an
+	 * element-list in ascending order of address to support merging
+	 * upon free.
+	 */
+	RTE_MEMAREA_ALG_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_alg 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;
+		/** User memory address, this field is valid only when the
+		 * source is set to be RTE_MEMAREA_SOURCE_USER.
+		 * Note: the address must align at least RTE_CACHE_LINE_SIZE.
+		 */
+		void *user_addr;
+		/** Source memarea, this field is valid only when the source is
+		 * set to be RTE_MEMAREA_SOURCE_MEMAREA.
+		 */
+		struct rte_memarea *src_memarea;
+	};
+};
+
+/**
+ * @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 */
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 c51cdc24fa..87d621e792 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v8 2/9] test/memarea: support memarea test
  2022-10-11 12:17 ` [PATCH v8 0/9] " Chengwen Feng
  2022-10-11 12:17   ` [PATCH v8 1/9] memarea: " Chengwen Feng
@ 2022-10-11 12:17   ` Chengwen Feng
  2022-10-11 12:17   ` [PATCH v8 3/9] memarea: support alloc/free/update-refcnt API Chengwen Feng
                     ` (6 subsequent siblings)
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-11 12:17 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports memarea test about API rte_memarea_create and
rte_memarea_destroy.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 168 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 171 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3d8d4c2dbe..38625ae1be 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1555,6 +1555,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index 396b133959..8943f24668 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -84,6 +84,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -200,6 +201,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..f02b33394f
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#ifdef RTE_EXEC_ENV_WINDOWS
+static int
+test_memarea(void)
+{
+	printf("memarea not supported on Windows, skipping test\n");
+	return TEST_SKIPPED;
+}
+
+#else
+
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) { \
+			printf("%s Failed\n", #test_func); \
+			fails++; \
+		} else { \
+			printf("%s Passed\n", #test_func); \
+		} \
+	} while (0)
+
+static int fails;
+
+static void
+test_memarea_prepare(void)
+{
+	fails = 0;
+}
+
+static int
+test_memarea_retcode(void)
+{
+	return fails > 0 ? -1 : 0;
+}
+
+static void
+test_memarea_init_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "test-memarea");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int i;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user memory address NULL */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for user memory address align invalid */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER;
+	for (i = 1; i < RTE_CACHE_LINE_SIZE; i++) {
+		init.user_addr = (void *)((uintptr_t)i);
+		ma = rte_memarea_create(&init);
+		RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	}
+
+	/* test for memarea NULL */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for alg invalid */
+	test_memarea_init_param(&init);
+	init.alg = RTE_MEMAREA_ALG_NEXTFIT + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for create with HEAP */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.numa_socket = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with user memory */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_USER;
+	init.user_addr = (void *)(((uintptr_t)user_buffer + RTE_CACHE_LINE_SIZE) &
+				  ~(RTE_CACHE_LINE_SIZE - 1));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	test_memarea_prepare();
+
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+
+	return test_memarea_retcode();
+}
+
+#endif /* !RTE_EXEC_ENV_WINDOWS */
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v8 3/9] memarea: support alloc/free/update-refcnt API
  2022-10-11 12:17 ` [PATCH v8 0/9] " Chengwen Feng
  2022-10-11 12:17   ` [PATCH v8 1/9] memarea: " Chengwen Feng
  2022-10-11 12:17   ` [PATCH v8 2/9] test/memarea: support memarea test Chengwen Feng
@ 2022-10-11 12:17   ` Chengwen Feng
  2022-10-11 15:58     ` Dmitry Kozlyuk
  2022-10-11 12:17   ` [PATCH v8 4/9] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
                     ` (5 subsequent siblings)
  8 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2022-10-11 12:17 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  10 ++
 lib/memarea/memarea_private.h         |   3 +
 lib/memarea/rte_memarea.c             | 155 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  56 ++++++++++
 lib/memarea/version.map               |   3 +
 5 files changed, 227 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 85ad57145f..a9c58dc44d 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,16 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
+The ``rte_memarea_update_refcnt()`` function is used to update the memory
+object's reference count, if the count reaches zero, the memory object will
+be freed to memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 59be9c1d00..f5accf2987 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -28,6 +28,9 @@ struct rte_memarea {
 	void                    *area_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
+
+	uint64_t alloc_fails;
+	uint64_t refcnt_check_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 85975029e2..8ad1c0acb5 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -4,6 +4,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -94,6 +95,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 		ptr = memarea_alloc_from_libc(init->total_sz);
 	else if (init->source == RTE_MEMAREA_SOURCE_USER)
 		ptr = init->user_addr;
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		ptr = rte_memarea_alloc(init->src_memarea, init->total_sz, 0);
 
 	if (ptr == NULL)
 		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
@@ -146,6 +149,8 @@ memarea_free_area(struct rte_memarea *ma)
 		rte_free(ma->area_addr);
 	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		rte_memarea_free(ma->init.src_memarea, ma->area_addr);
 }
 
 void
@@ -156,3 +161,153 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(ma);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+static inline bool
+memarea_whether_add_node(size_t free_size, size_t need_size)
+{
+	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
+	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
+}
+
+static inline void
+memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
+{
+	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
+	struct memarea_elem *new_elem;
+	new_elem = (struct memarea_elem *)RTE_PTR_ADD(elem, sizeof(struct memarea_elem) +
+							    align_size);
+	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+	new_elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+	new_elem->refcnt = 0;
+	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
+	TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
+	elem->size = align_size;
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	struct memarea_elem *elem;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0))
+		return NULL;
+
+	memarea_lock(ma);
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (unlikely(elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC))
+			break;
+		if (elem->size < size)
+			continue;
+		if (memarea_whether_add_node(elem->size, size))
+			memarea_add_node(ma, elem, size);
+		elem->magic = MEMAREA_ALLOCATED_ELEM_MAGIC;
+		elem->cookie = cookie;
+		elem->refcnt = 1;
+		TAILQ_REMOVE(&ma->free_list, elem, free_node);
+		ptr = RTE_PTR_ADD(elem, sizeof(struct memarea_elem));
+		break;
+	}
+	if (unlikely(ptr == NULL))
+		ma->alloc_fails++;
+	memarea_unlock(ma);
+
+	return ptr;
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	rte_memarea_update_refcnt(ma, ptr, -1);
+}
+
+static inline void
+memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
+		   struct memarea_elem *next, bool del_next_from_free,
+		   bool add_curr_to_free)
+{
+	curr->size += next->size + sizeof(struct memarea_elem);
+	next->size = 0;
+	next->magic = 0;
+	next->cookie = 0;
+	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
+	if (del_next_from_free)
+		TAILQ_REMOVE(&ma->free_list, next, free_node);
+	if (add_curr_to_free) {
+		curr->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+		curr->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
+	}
+}
+
+static inline void
+memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
+{
+	struct memarea_elem *prev, *next;
+	bool merged = false;
+	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
+	next = TAILQ_NEXT(elem, elem_node);
+	if (prev != NULL && prev->refcnt == 0) {
+		memarea_merge_node(ma, prev, elem, false, false);
+		elem = prev;
+		merged = true;
+	}
+	if (next != NULL && next->refcnt == 0) {
+		memarea_merge_node(ma, elem, next, true, !merged);
+		merged = true;
+	}
+	if (!merged) {
+		elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+		elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	}
+}
+
+void
+rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
+{
+	struct memarea_elem *elem = (struct memarea_elem *)RTE_PTR_SUB(ptr,
+							    sizeof(struct memarea_elem));
+
+	if (unlikely(ma == NULL || ptr == NULL))
+		return;
+
+	memarea_lock(ma);
+	if (unlikely(elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail!\n",
+			ma->init.name, elem->magic);
+		memarea_unlock(ma);
+		return;
+	}
+
+	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
+		RTE_LOG(ERR, MEMAREA,
+			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
+			ma->init.name, elem->cookie, elem->refcnt, value);
+		ma->refcnt_check_fails++;
+		if (elem->refcnt > 0)
+			elem->refcnt += value;
+		memarea_unlock(ma);
+		return;
+	}
+
+	elem->refcnt += value;
+	if (elem->refcnt == 0)
+		memarea_free_elem(ma, elem);
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index ac2d63a4d3..f866fa7b67 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -129,6 +129,62 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ * @param cookie
+ *   User-provided footprint which could used to debug memory leak.
+ *
+ * @return
+ *   Non-NULL on success. Otherwise NULL is returned.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be freed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Update memory's refcnt.
+ *
+ * Update one memory object's refcnt.
+ * When refcnt is updated to be zero, the memory object is freed.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be updated refcnt.
+ * @param value
+ *   The value which need be updated.
+ */
+__rte_experimental
+void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..a0026fc5f9 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,11 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
+	rte_memarea_update_refcnt;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v8 4/9] test/memarea: support alloc/free/update-refcnt test
  2022-10-11 12:17 ` [PATCH v8 0/9] " Chengwen Feng
                     ` (2 preceding siblings ...)
  2022-10-11 12:17   ` [PATCH v8 3/9] memarea: support alloc/free/update-refcnt API Chengwen Feng
@ 2022-10-11 12:17   ` Chengwen Feng
  2022-10-11 12:17   ` [PATCH v8 5/9] memarea: support dump API Chengwen Feng
                     ` (4 subsequent siblings)
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-11 12:17 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_update_refcnt() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 150 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 149 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index f02b33394f..5f7868f8f6 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -57,6 +57,12 @@ test_memarea_init_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -122,8 +128,8 @@ static int
 test_memarea_create_destroy(void)
 {
 	uint8_t user_buffer[MEMAREA_TEST_DEFAULT_SIZE + RTE_CACHE_LINE_SIZE];
+	struct rte_memarea *ma, *src_ma;
 	struct rte_memarea_param init;
-	struct rte_memarea *ma;
 
 	/* test for create with HEAP */
 	test_memarea_init_param(&init);
@@ -149,6 +155,145 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with another memarea */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	src_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(src_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.src_memarea = src_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(src_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE - RTE_CACHE_LINE_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	ptr[1] = rte_memarea_alloc(ma, 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test invalid parameters with update-refcnt */
+	rte_memarea_update_refcnt(NULL, (void *)(uintptr_t)1, 0);
+	rte_memarea_update_refcnt(ma, NULL, 0);
+	rte_memarea_update_refcnt(NULL, NULL, 0);
+
+	/* test free with refcnt fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	/* test update refcnt with fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_update_refcnt(ma, ptr, -2);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[ALLOC_MAX_NUM];
+	int i;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1, 0);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, NULL);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -159,6 +304,9 @@ test_memarea(void)
 
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v8 5/9] memarea: support dump API
  2022-10-11 12:17 ` [PATCH v8 0/9] " Chengwen Feng
                     ` (3 preceding siblings ...)
  2022-10-11 12:17   ` [PATCH v8 4/9] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
@ 2022-10-11 12:17   ` Chengwen Feng
  2022-10-11 12:17   ` [PATCH v8 6/9] test/memarea: support dump test Chengwen Feng
                     ` (3 subsequent siblings)
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-11 12:17 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +
 lib/memarea/rte_memarea.c             | 98 +++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 21 ++++++
 lib/memarea/version.map               |  1 +
 4 files changed, 123 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index a9c58dc44d..8b616b57e6 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -43,6 +43,9 @@ The ``rte_memarea_update_refcnt()`` function is used to update the memory
 object's reference count, if the count reaches zero, the memory object will
 be freed to memarea.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 8ad1c0acb5..163ebfa792 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2022 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/queue.h>
@@ -311,3 +312,100 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		memarea_free_elem(ma, elem);
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_USER)
+		return "user";
+	else if (source == RTE_MEMAREA_SOURCE_MEMAREA)
+		return "memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_alg alg)
+{
+	if (alg == RTE_MEMAREA_ALG_NEXTFIT)
+		return "nextfit";
+	else
+		return "unknown";
+}
+
+static uint32_t
+memarea_elem_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC &&
+		    elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC)
+			break;
+		num++;
+	}
+
+	return num;
+}
+
+static uint32_t
+memarea_free_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC)
+			break;
+		num++;
+	}
+
+	return num;
+}
+
+static void
+memarea_dump_all(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_elem *elem;
+
+	fprintf(f, "  regions:\n");
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC &&
+		    elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC) {
+			fprintf(f, "    magic: 0x%x chech fail!\n", elem->magic);
+			break;
+		}
+		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
+			elem->size, elem->cookie, elem->refcnt);
+	}
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		fprintf(f, "  source-memarea: %s\n", ma->init.src_memarea->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
+	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
+	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
+	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (dump_all)
+		memarea_dump_all(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index f866fa7b67..5e3f23700b 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -185,6 +185,27 @@ void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 __rte_experimental
 void rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index a0026fc5f9..d8ddd93c13 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 	rte_memarea_update_refcnt;
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v8 6/9] test/memarea: support dump test
  2022-10-11 12:17 ` [PATCH v8 0/9] " Chengwen Feng
                     ` (4 preceding siblings ...)
  2022-10-11 12:17   ` [PATCH v8 5/9] memarea: support dump API Chengwen Feng
@ 2022-10-11 12:17   ` Chengwen Feng
  2022-10-11 12:17   ` [PATCH v8 7/9] memarea: support backup memory mechanism Chengwen Feng
                     ` (2 subsequent siblings)
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-11 12:17 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_dump() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 5f7868f8f6..50b92db9b3 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -297,6 +297,38 @@ test_memarea_alloc_free(void)
 	return 0;
 }
 
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -307,6 +339,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v8 7/9] memarea: support backup memory mechanism
  2022-10-11 12:17 ` [PATCH v8 0/9] " Chengwen Feng
                     ` (5 preceding siblings ...)
  2022-10-11 12:17   ` [PATCH v8 6/9] test/memarea: support dump test Chengwen Feng
@ 2022-10-11 12:17   ` Chengwen Feng
  2022-10-11 15:58     ` Dmitry Kozlyuk
  2022-10-11 12:17   ` [PATCH v8 8/9] test/memarea: support backup memory test Chengwen Feng
  2022-10-11 12:17   ` [PATCH v8 9/9] app/test: add memarea to malloc-perf-autotest Chengwen Feng
  8 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2022-10-11 12:17 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch adds a memarea backup mechanism, where an allocation request
which cannot be met by the current memarea is deferred to its backup
memarea.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  4 ++
 lib/memarea/memarea_private.h         |  3 ++
 lib/memarea/rte_memarea.c             | 67 ++++++++++++++++++++++++++-
 lib/memarea/rte_memarea.h             | 10 ++++
 4 files changed, 83 insertions(+), 1 deletion(-)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 8b616b57e6..6b180bb0e4 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -25,6 +25,10 @@ The main features are as follows:
 
 * It supports MT-safe as long as it's specified at creation time.
 
+* It provides backup memory mechanism, the memarea could use another memarea
+  as a backup. It will attempts to allocate object from backup memarea when
+  the current memarea failed to allocate.
+
 Library API Overview
 --------------------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index f5accf2987..062e967250 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -14,6 +14,7 @@
 struct memarea_elem {
 	TAILQ_ENTRY(memarea_elem) elem_node;
 	TAILQ_ENTRY(memarea_elem) free_node;
+	struct rte_memarea       *owner;
 	size_t   size;
 	uint32_t magic;
 	uint32_t cookie;
@@ -26,11 +27,13 @@ struct rte_memarea {
 	struct rte_memarea_param init;
 	rte_spinlock_t           lock;
 	void                    *area_addr;
+	void                    *top_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
 
 	uint64_t alloc_fails;
 	uint64_t refcnt_check_fails;
+	uint64_t bak_alloc_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 163ebfa792..3de4297cf7 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -132,9 +132,11 @@ rte_memarea_create(const struct rte_memarea_param *init)
 	TAILQ_INIT(&ma->elem_list);
 	TAILQ_INIT(&ma->free_list);
 	ma->area_addr = addr;
+	ma->top_addr = RTE_PTR_ADD(addr, init->total_sz - 1);
 	elem = addr;
 	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
 	TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	elem->owner = NULL;
 	elem->size = init->total_sz - sizeof(struct memarea_elem);
 	elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
 	elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
@@ -154,11 +156,41 @@ memarea_free_area(struct rte_memarea *ma)
 		rte_memarea_free(ma->init.src_memarea, ma->area_addr);
 }
 
+static inline void memarea_lock(struct rte_memarea *ma);
+static inline void memarea_unlock(struct rte_memarea *ma);
+static inline void memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem);
+
+static void
+memarea_free_owner_objs(struct rte_memarea *ma, struct rte_memarea *owner)
+{
+	struct memarea_elem *elem, *tmp_elem;
+
+	memarea_lock(ma);
+	/* The TAILQ_FOREACH_SAFE is undefined in sys/queue.h, so extend it here. */
+	for (elem = TAILQ_FIRST(&ma->elem_list);
+	     elem && (tmp_elem = TAILQ_NEXT(elem, elem_node), 1);
+	     elem = tmp_elem) {
+		if (elem->owner != owner)
+			continue;
+		elem->refcnt = 0;
+		memarea_free_elem(ma, elem);
+	}
+	if (ma->init.bak_memarea != NULL)
+		memarea_free_owner_objs(ma->init.bak_memarea, owner);
+	memarea_unlock(ma);
+}
+
 void
 rte_memarea_destroy(struct rte_memarea *ma)
 {
 	if (ma == NULL)
 		return;
+	if (ma->init.bak_memarea != NULL) {
+		/* Some objects are allocated from backup memarea, these objects need to be
+		 * freed when the memarea is destroyed.
+		 */
+		memarea_free_owner_objs(ma->init.bak_memarea, ma);
+	}
 	memarea_free_area(ma);
 	rte_free(ma);
 }
@@ -191,6 +223,7 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
 	struct memarea_elem *new_elem;
 	new_elem = (struct memarea_elem *)RTE_PTR_ADD(elem, sizeof(struct memarea_elem) +
 							    align_size);
+	new_elem->owner = NULL;
 	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
 	new_elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
 	new_elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
@@ -200,6 +233,23 @@ memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_
 	elem->size = align_size;
 }
 
+static inline void
+memarea_mark_owner(struct rte_memarea *ma, void *ptr)
+{
+	struct memarea_elem *elem;
+	elem = (struct memarea_elem *)RTE_PTR_SUB(ptr, sizeof(struct memarea_elem));
+	elem->owner = ma;
+}
+
+static inline void *
+memarea_alloc_backup(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	void *ptr = rte_memarea_alloc(ma->init.bak_memarea, size, cookie);
+	if (unlikely(ptr == NULL))
+		ma->bak_alloc_fails++;
+	return ptr;
+}
+
 void *
 rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 {
@@ -224,7 +274,11 @@ rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
 		ptr = RTE_PTR_ADD(elem, sizeof(struct memarea_elem));
 		break;
 	}
-	if (unlikely(ptr == NULL))
+	if (unlikely(ptr == NULL && ma->init.bak_memarea != NULL))
+		ptr = memarea_alloc_backup(ma, size, cookie);
+	if (likely(ptr != NULL))
+		memarea_mark_owner(ma, ptr);
+	else
 		ma->alloc_fails++;
 	memarea_unlock(ma);
 
@@ -261,6 +315,7 @@ memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
 {
 	struct memarea_elem *prev, *next;
 	bool merged = false;
+	elem->owner = NULL;
 	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
 	next = TAILQ_NEXT(elem, elem_node);
 	if (prev != NULL && prev->refcnt == 0) {
@@ -296,6 +351,12 @@ rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
 		return;
 	}
 
+	if (unlikely(ptr < ma->area_addr || ptr > ma->top_addr)) {
+		rte_memarea_update_refcnt(ma->init.bak_memarea, ptr, value);
+		memarea_unlock(ma);
+		return;
+	}
+
 	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
 		RTE_LOG(ERR, MEMAREA,
 			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
@@ -399,10 +460,14 @@ rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
 	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
 	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
 	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	if (ma->init.bak_memarea)
+		fprintf(f, "  backup-memarea-name: %s\n", ma->init.bak_memarea->init.name);
 	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
 	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
 	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
 	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (ma->init.bak_memarea)
+		fprintf(f, "  backup_alloc_fails: %" PRIu64 "\n", ma->bak_alloc_fails);
 	if (dump_all)
 		memarea_dump_all(ma, f);
 	memarea_unlock(ma);
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 5e3f23700b..abfff3d996 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -28,6 +28,12 @@
  *   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.
+ *
+ * - It provides backup memory mechanism, the memarea could use another memarea
+ *   as a backup. It will attempts to allocate object from backup memarea when
+ *   the current memarea failed to allocate.
+ *   @note If the backup of memarea-A is memarea-B, then must destroy memarea-A
+ *   before destroying memarae-B.
  */
 
 #include <stdbool.h>
@@ -96,6 +102,10 @@ struct rte_memarea_param {
 		 */
 		struct rte_memarea *src_memarea;
 	};
+	/** Backup memarea, which is used to allocate object from when the current
+	 * memarea failed to allocate.
+	 */
+	struct rte_memarea *bak_memarea;
 };
 
 /**
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v8 8/9] test/memarea: support backup memory test
  2022-10-11 12:17 ` [PATCH v8 0/9] " Chengwen Feng
                     ` (6 preceding siblings ...)
  2022-10-11 12:17   ` [PATCH v8 7/9] memarea: support backup memory mechanism Chengwen Feng
@ 2022-10-11 12:17   ` Chengwen Feng
  2022-10-11 12:17   ` [PATCH v8 9/9] app/test: add memarea to malloc-perf-autotest Chengwen Feng
  8 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-11 12:17 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports backup memory mechanism test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 93 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 93 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 50b92db9b3..c12af6338e 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -329,6 +329,97 @@ test_memarea_dump(void)
 	return 0;
 }
 
+static int
+test_memarea_backup(void)
+{
+	struct rte_memarea *ma, *bak_ma;
+	struct rte_memarea_param init;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	strcat(init.name, "_backup");
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	bak_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(bak_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE >> 2;
+	init.bak_memarea = bak_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for backup */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 3, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+	rte_memarea_free(ma, ptr);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(bak_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_backup_destroy(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea *ma, *bak_ma, *bakbak_ma;
+	struct rte_memarea_param init;
+	void *ptr[ALLOC_MAX_NUM];
+	int i;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	strcat(init.name, "_bakbak");
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE * ALLOC_MAX_NUM;
+	bakbak_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(bakbak_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	strcat(init.name, "_bak");
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.bak_memarea = bakbak_ma;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE * ALLOC_MAX_NUM / 2;
+	bak_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(bak_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE >> 2;
+	init.bak_memarea = bak_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* alloc for multiple count */
+	for (i = 0; i < ALLOC_MAX_NUM; i++) {
+		ptr[i] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+		RTE_TEST_ASSERT(ptr[i] != NULL, "Expected Non-NULL");
+		rte_memarea_update_refcnt(ma, ptr[i], i + 1);
+	}
+	(void)rte_memarea_dump(ma, stderr, true);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+	(void)rte_memarea_dump(bakbak_ma, stderr, true);
+
+	/* try destroy outmost memarea */
+	rte_memarea_destroy(ma);
+	(void)rte_memarea_dump(bak_ma, stderr, true);
+	(void)rte_memarea_dump(bakbak_ma, stderr, true);
+
+	rte_memarea_destroy(bak_ma);
+	rte_memarea_destroy(bakbak_ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -340,6 +431,8 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 	MEMAREA_TEST_API_RUN(test_memarea_dump);
+	MEMAREA_TEST_API_RUN(test_memarea_backup);
+	MEMAREA_TEST_API_RUN(test_memarea_backup_destroy);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v8 9/9] app/test: add memarea to malloc-perf-autotest
  2022-10-11 12:17 ` [PATCH v8 0/9] " Chengwen Feng
                     ` (7 preceding siblings ...)
  2022-10-11 12:17   ` [PATCH v8 8/9] test/memarea: support backup memory test Chengwen Feng
@ 2022-10-11 12:17   ` Chengwen Feng
  2022-10-11 15:58     ` Dmitry Kozlyuk
  8 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2022-10-11 12:17 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch adds memarea to malloc_perf_autotest.

Test platform: Kunpeng920
Test command: dpdk-test -a 0000:7d:00.3 -l 10-12
Test result:
USER1: Performance: rte_memarea
USER1:    Size (B)   Runs  Alloc (us) Free (us) Total (us)  memset (us)
USER1:          64  10000        0.03      0.03       0.06         0.01
USER1:         128  10000        0.02      0.03       0.05         0.01
USER1:        1024  10000        0.03      0.05       0.07         0.20
USER1:        4096  10000        0.03      0.05       0.07         0.34
USER1:       65536  10000        0.10      0.08       0.18         2.14
USER1:     1048576    644        0.10      0.04       0.14        29.07
USER1:     2097152    322        0.10      0.04       0.14        57.50
USER1:     4194304    161        0.12      0.04       0.15       114.50
USER1:    16777216     40        0.11      0.04       0.15       456.09
USER1:  1073741824 Interrupted: out of memory. [1]

Compared with rte_malloc:
USER1: Performance: rte_malloc
USER1:    Size (B)   Runs  Alloc (us) Free (us) Total (us)  memset (us)
USER1:          64  10000        0.14      0.07       0.21         0.01
USER1:         128  10000        0.10      0.05       0.15         0.01
USER1:        1024  10000        0.11      0.18       0.29         0.21
USER1:        4096  10000        0.13      0.39       0.53         0.35
USER1:       65536  10000        0.17      2.27       2.44         2.15
USER1:     1048576  10000       37.21     71.63     108.84        29.08
USER1:     2097152  10000     8831.15    160.02    8991.17        63.52
USER1:     4194304  10000    47131.88    413.75   47545.62       173.79
USER1:    16777216   4221   119604.60   2209.73  121814.34       964.42
USER1:  1073741824     31   335058.32 223369.31  558427.63     62440.87

[1] The total-size of the memarea is restricted to avoid creation
failed.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_malloc_perf.c | 55 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 54 insertions(+), 1 deletion(-)

diff --git a/app/test/test_malloc_perf.c b/app/test/test_malloc_perf.c
index ccec43ae84..a8b4531fe3 100644
--- a/app/test/test_malloc_perf.c
+++ b/app/test/test_malloc_perf.c
@@ -7,10 +7,12 @@
 #include <rte_cycles.h>
 #include <rte_errno.h>
 #include <rte_malloc.h>
+#include <rte_memarea.h>
 #include <rte_memzone.h>
 
 #include "test.h"
 
+#define PERFTEST_MAX_RUNS	10000
 #define TEST_LOG(level, ...) RTE_LOG(level, USER1, __VA_ARGS__)
 
 typedef void * (alloc_t)(const char *name, size_t size, unsigned int align);
@@ -147,10 +149,52 @@ memzone_free(void *addr)
 	rte_memzone_free((struct rte_memzone *)addr);
 }
 
+static struct rte_memarea *test_ma;
+
+static int
+memarea_pre_env(void)
+{
+	struct rte_memarea_param init = { 0 };
+	snprintf(init.name, sizeof(init.name), "perftest");
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.alg = RTE_MEMAREA_ALG_NEXTFIT;
+	init.total_sz = PERFTEST_MAX_RUNS * KB * 66; /* test for max 64KB (add 2KB for meta) */
+	init.mt_safe = 1;
+	init.numa_socket = SOCKET_ID_ANY;
+	init.bak_memarea = NULL;
+	test_ma = rte_memarea_create(&init);
+	if (test_ma == NULL) {
+		fprintf(stderr, "memarea create failed, skip memarea perftest!\n");
+		return -1;
+	}
+	return 0;
+}
+
+static void
+memarea_clear_env(void)
+{
+	rte_memarea_destroy(test_ma);
+	test_ma = NULL;
+}
+
+static void *
+memarea_alloc(const char *name, size_t size, unsigned int align)
+{
+	RTE_SET_USED(name);
+	RTE_SET_USED(align);
+	return rte_memarea_alloc(test_ma, size, 0);
+}
+
+static void
+memarea_free(void *addr)
+{
+	rte_memarea_free(test_ma, addr);
+}
+
 static int
 test_malloc_perf(void)
 {
-	static const size_t MAX_RUNS = 10000;
+	static const size_t MAX_RUNS = PERFTEST_MAX_RUNS;
 
 	double memset_us_gb = 0;
 
@@ -168,6 +212,15 @@ test_malloc_perf(void)
 			NULL, memset_us_gb, RTE_MAX_MEMZONE - 1) < 0)
 		return -1;
 
+	if (memarea_pre_env() < 0)
+		return 0;
+	if (test_alloc_perf("rte_memarea", memarea_alloc, memarea_free,
+			memset, memset_us_gb, MAX_RUNS) < 0) {
+		memarea_clear_env();
+		return -1;
+	}
+	memarea_clear_env();
+
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v5 01/10] memarea: introduce memarea library
  2022-10-10 23:33           ` fengchengwen
@ 2022-10-11 15:35             ` Mattias Rönnblom
  0 siblings, 0 replies; 222+ messages in thread
From: Mattias Rönnblom @ 2022-10-11 15:35 UTC (permalink / raw)
  To: fengchengwen, datshan, david.marchand, mb, anatoly.burakov,
	dmitry.kozliuk, jerinjacobk
  Cc: thomas, dev

On 2022-10-11 01:33, fengchengwen wrote:
> On 2022/10/11 0:53, Mattias Rönnblom wrote:
>> On 2022-10-08 09:53, fengchengwen wrote:
>>> Hi Mattias, Thanks for your review, most will fix in v6.
>>>
>>> On 2022/10/7 4:15, Mattias Rönnblom wrote:
>>>> On 2022-10-05 06:09, datshan wrote:
>>>>> From: Chengwen Feng <fengchengwen@huawei.com>
>>>>>
>>>>> The memarea library is an allocator of variable-size object which 
>>>>> based
>>>>> on a memory region.
>>>>>
>>>>> This patch provides create/destroy API.
>>>>>
>>>>> Signed-off-by: Chengwen Feng <fengchengwen@huawei.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  |  39 ++++++
>>>>>    doc/guides/rel_notes/release_22_11.rst |   6 +
>>>>>    lib/eal/common/eal_common_log.c        |   1 +
>>>>>    lib/eal/include/rte_log.h              |   1 +
>>>>>    lib/memarea/memarea_private.h          |  30 +++++
>>>>>    lib/memarea/meson.build                |  16 +++
>>>>>    lib/memarea/rte_memarea.c              | 157 
>>>>> +++++++++++++++++++++++++
>>>>>    lib/memarea/rte_memarea.h              | 145 
>>>>> +++++++++++++++++++++++
>>>>>    lib/memarea/version.map                |  12 ++
>>>>>    lib/meson.build                        |   1 +
>>>>>    14 files changed, 417 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 a55b379d73..b9c638221d 100644
>>>>> --- a/MAINTAINERS
>>>>> +++ b/MAINTAINERS
>>>>> @@ -1550,6 +1550,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>
>>>>> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
>>>>> index de488c7abf..24456604f8 100644
>>>>> --- a/doc/api/doxy-api-index.md
>>>>> +++ b/doc/api/doxy-api-index.md
>>>>> @@ -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),
>>>>> diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
>>>>> index f0886c3bd1..8334ebcbd6 100644
>>>>> --- a/doc/api/doxy-api.conf.in
>>>>> +++ b/doc/api/doxy-api.conf.in
>>>>> @@ -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 \
>>>>> diff --git a/doc/guides/prog_guide/index.rst 
>>>>> b/doc/guides/prog_guide/index.rst
>>>>> index 8564883018..e9015d65e3 100644
>>>>> --- a/doc/guides/prog_guide/index.rst
>>>>> +++ b/doc/guides/prog_guide/index.rst
>>>>> @@ -37,6 +37,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..b96dad15f6
>>>>> --- /dev/null
>>>>> +++ b/doc/guides/prog_guide/memarea_lib.rst
>>>>> @@ -0,0 +1,39 @@
>>>>> +..  SPDX-License-Identifier: BSD-3-Clause
>>>>> +    Copyright(c) 2022 HiSilicon Limited
>>>>> +
>>>>> +Memarea Library
>>>>> +===============
>>>>> +
>>>>> +Introduction
>>>>> +------------
>>>>> +
>>>>> +The memarea library provides an allocator of variable-size 
>>>>> objects, it is
>>>>> +oriented towards the application layer, which could provides 
>>>>> 'region-based
>>>>> +memory management' function [1].
>>>>> +
>>>>> +The main features are as follows:
>>>>> +
>>>>> +* The default aligement size is ``RTE_CACHE_LINE_SIZE``.
>>>>> +
>>>>> +* The memory region can be initialized from the following memory 
>>>>> sources:
>>>>> +  a) RTE memory: e.g. invoke ``rte_malloc_socket`` to obtain. b) 
>>>>> System API:
>>>>> +  e.g. invoke posix_memalign to obtain. c) User provided address: 
>>>>> it can be from
>>>>> +  extendedd memory as long as it is available. d) User provided 
>>>>> memarea: it can
>>>>> +  be from another memarea.
>>>>> +
>>>>> +* It provides refcnt feature which could be useful in multi-reader 
>>>>> scenario.
>>>>> +
>>>>> +* 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.
>>>>> +
>>>>> +Reference
>>>>> +---------
>>>>> +
>>>>> +[1] https://en.wikipedia.org/wiki/Region-based_memory_management
>>>>> diff --git a/doc/guides/rel_notes/release_22_11.rst 
>>>>> b/doc/guides/rel_notes/release_22_11.rst
>>>>> index 5d8ef669b8..4c1f760b98 100644
>>>>> --- a/doc/guides/rel_notes/release_22_11.rst
>>>>> +++ b/doc/guides/rel_notes/release_22_11.rst
>>>>> @@ -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, which could provides 
>>>>> 'region-based memory
>>>>> +  management' function.
>>>>> +
>>>>>    * **Added configuration for asynchronous flow connection 
>>>>> tracking.**
>>>>>        Added connection tracking action number hint to 
>>>>> ``rte_flow_configure``
>>>>> diff --git a/lib/eal/common/eal_common_log.c 
>>>>> b/lib/eal/common/eal_common_log.c
>>>>> index bd7b188ceb..3d62af59c6 100644
>>>>> --- a/lib/eal/common/eal_common_log.c
>>>>> +++ b/lib/eal/common/eal_common_log.c
>>>>> @@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
>>>>>        {RTE_LOGTYPE_EFD,        "lib.efd"},
>>>>>        {RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
>>>>>        {RTE_LOGTYPE_GSO,        "lib.gso"},
>>>>> +    {RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
>>>>>        {RTE_LOGTYPE_USER1,      "user1"},
>>>>>        {RTE_LOGTYPE_USER2,      "user2"},
>>>>>        {RTE_LOGTYPE_USER3,      "user3"},
>>>>> diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
>>>>> index 25ce42cdfc..708f3a39dd 100644
>>>>> --- a/lib/eal/include/rte_log.h
>>>>> +++ b/lib/eal/include/rte_log.h
>>>>> @@ -48,6 +48,7 @@ extern "C" {
>>>>>    #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
>>>>>    #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
>>>>>    #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
>>>>> +#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
>>>>>      /* these log types can be used in an application */
>>>>>    #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
>>>>> diff --git a/lib/memarea/memarea_private.h 
>>>>> b/lib/memarea/memarea_private.h
>>>>> new file mode 100644
>>>>> index 0000000000..c76392d3e6
>>>>> --- /dev/null
>>>>> +++ b/lib/memarea/memarea_private.h
>>>>> @@ -0,0 +1,30 @@
>>>>> +/* SPDX-License-Identifier: BSD-3-Clause
>>>>> + * Copyright(c) 2022 HiSilicon Limited
>>>>> + */
>>>>> +
>>>>> +#ifndef MEMAREA_PRIVATE_H
>>>>> +#define MEMAREA_PRIVATE_H
>>>>> +
>>>>> +#include <rte_memarea.h>
>>>>> +
>>>>> +#define MEMAREA_FREE_ELEM_COOKIE    0xFFFFFFFF
>>>>> +
>>>>> +struct memarea_elem {
>>>>> +    size_t   size;
>>>>> +    uint32_t cookie;
>>>>> +    int32_t  refcnt; /* Non-zero indicates that it has been 
>>>>> allocated */
>>>>> +    TAILQ_ENTRY(memarea_elem) elem_node;
>>>>> +    TAILQ_ENTRY(memarea_elem) free_node;
>>>>> +} __rte_cache_aligned;
>>>>> +
>>>>
>>>> Why is the elem type cache line aligned? Need the elem data start be 
>>>> cache line aligned?
>>>
>>> Yes, the elem data align at cache-line default.
>>>
>>>>
>>>>> +TAILQ_HEAD(memarea_elem_list, memarea_elem);
>>>>> +
>>>>> +struct rte_memarea {
>>>>> +    struct rte_memarea_param init;
>>>>> +    rte_spinlock_t           lock;
>>>>> +    void                    *area_addr;
>>>>> +    struct memarea_elem_list elem_list;
>>>>> +    struct memarea_elem_list free_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..0a74fb4cd1
>>>>> --- /dev/null
>>>>> +++ b/lib/memarea/meson.build
>>>>> @@ -0,0 +1,16 @@
>>>>> +# SPDX-License-Identifier: BSD-3-Clause
>>>>> +# Copyright(c) 2022 HiSilicon Limited
>>>>> +
>>>>> +if is_windows
>>>>> +    build = false
>>>>> +    reason = 'not supported on Windows'
>>>>> +    subdir_done()
>>>>> +endif
>>>>> +
>>>>> +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..868da7661d
>>>>> --- /dev/null
>>>>> +++ b/lib/memarea/rte_memarea.c
>>>>> @@ -0,0 +1,157 @@
>>>>> +/* SPDX-License-Identifier: BSD-3-Clause
>>>>> + * Copyright(c) 2022 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"
>>>>> +
>>>>> +static int
>>>>> +memarea_check_param(const struct rte_memarea_param *init)
>>>>> +{
>>>>> +    size_t len;
>>>>> +
>>>>> +    if (init == NULL) {
>>>>> +        RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +
>>>>> +    len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
>>>>> +    if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
>>>>> +        RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", 
>>>>> len);
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +
>>>>> +    if (init->source != RTE_MEMAREA_SOURCE_RTE_MEMORY &&
>>>>> +        init->source != RTE_MEMAREA_SOURCE_SYSTEM_API &&
>>>>> +        init->source != RTE_MEMAREA_SOURCE_USER_ADDR &&
>>>>> +        init->source != RTE_MEMAREA_SOURCE_USER_MEMAREA) {
>>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not 
>>>>> supported!\n",
>>>>> +            init->name, init->source);
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +
>>>>> +    if (init->total_sz <= sizeof(struct memarea_elem)) {
>>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too 
>>>>> small!\n",
>>>>> +            init->name, init->total_sz);
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +
>>>>> +    if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR && 
>>>>> init->user_addr == NULL) {
>>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr is 
>>>>> NULL!\n", init->name);
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +
>>>>> +    if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR &&
>>>>> +        ((uintptr_t)init->user_addr & (RTE_CACHE_LINE_SIZE - 1))) {
>>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s user provided addr 
>>>>> should align: %d!\n",
>>>>> +            init->name, RTE_CACHE_LINE_SIZE);
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +
>>>>> +    if (init->source == RTE_MEMAREA_SOURCE_USER_MEMAREA && 
>>>>> init->user_memarea == NULL) {
>>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s user provided memarea 
>>>>> is NULL!\n", init->name);
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +
>>>>> +    if (init->alg != RTE_MEMAREA_ALG_DEFAULT) {
>>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
>>>>> +            init->name, init->alg);
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static void *
>>>>> +memarea_alloc_from_system_api(size_t size)
>>>>> +{
>>>>> +    void *ptr = NULL;
>>>>> +    int ret;
>>>>> +
>>>>> +    ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
>>>>> +    if (ret)
>>>>> +        return NULL;
>>>>> +    return ptr;
>>>>> +}
>>>>> +
>>>>> +static void *
>>>>> +memarea_alloc_area(const struct rte_memarea_param *init)
>>>>> +{
>>>>> +    void *ptr = NULL;
>>>>> +
>>>>> +    if (init->source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
>>>>
>>>> Delete MEMORY. Of course it's memory. What else? If you want to make 
>>>> it clear it's from the RTE heap, it should spell out HEAP. Or MALLOC.
>>>
>>> HEAP seem better.
>>>
>>>>
>>>>> +        ptr = rte_malloc_socket(NULL, init->total_sz, 
>>>>> RTE_CACHE_LINE_SIZE,
>>>>> +                    init->numa_socket);
>>>>> +    else if (init->source == RTE_MEMAREA_SOURCE_SYSTEM_API)
>>>>> +        ptr = memarea_alloc_from_system_api(init->total_sz);
>>>>
>>>> "SYSTEM_API" doesn't strike me as a good name.
>>>>
>>>> RTE_MEMAREA_SOURCE_LIBC
>>>
>>> LIBC seem better.
>>>
>>>> RTE_MEMAREA_SOURCE_STD_HEAP
>>>> or at least remove API so it'll be
>>>> RTE_MEMAREA_SOURCE_SYSTEM
>>>>
>>>>> +    else if (init->source == RTE_MEMAREA_SOURCE_USER_ADDR)
>>>>
>>>> I would delete "ADDR".
>>>
>>> +1
>>>
>>>>
>>>>> +        ptr = init->user_addr;
>>>>> +
>>>>> +    if (ptr == NULL)
>>>>> +        RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area 
>>>>> fail!\n", init->name);
>>>>> +
>>>>> +    return ptr;
>>>>> +}
>>>>> +
>>>>> +struct rte_memarea *
>>>>> +rte_memarea_create(const struct rte_memarea_param *init)
>>>>> +{
>>>>> +    struct memarea_elem *elem;
>>>>> +    struct rte_memarea *ma;
>>>>> +    void *addr;
>>>>> +    int ret;
>>>>> +
>>>>> +    ret = memarea_check_param(init);
>>>>> +    if (ret)
>>>>> +        return NULL;
>>>>> +
>>>>> +    addr = memarea_alloc_area(init);
>>>>> +    if (addr == NULL)
>>>>> +        return NULL;
>>>>> +
>>>>> +    ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), 
>>>>> RTE_CACHE_LINE_SIZE);
>>>>> +    if (ma == NULL) {
>>>>> +        RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj 
>>>>> fail!\n", init->name);
>>>>> +        return NULL;
>>>>> +    }
>>>>> +
>>>>> +    ma->init = *init;
>>>>> +    rte_spinlock_init(&ma->lock);
>>>>> +    TAILQ_INIT(&ma->elem_list);
>>>>> +    TAILQ_INIT(&ma->free_list);
>>>>> +    ma->area_addr = addr;
>>>>> +    elem = addr;
>>>>> +    elem->size = init->total_sz - sizeof(struct memarea_elem);
>>>>> +    elem->cookie = MEMAREA_FREE_ELEM_COOKIE;
>>>>> +    elem->refcnt = 0;
>>>>> +    TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
>>>>> +    TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
>>>>> +
>>>>> +    return ma;
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +memarea_free_area(struct rte_memarea *ma)
>>>>> +{
>>>>> +    if (ma->init.source == RTE_MEMAREA_SOURCE_RTE_MEMORY)
>>>>> +        rte_free(ma->area_addr);
>>>>> +    else if (ma->init.source == RTE_MEMAREA_SOURCE_SYSTEM_API)
>>>>> +        free(ma->area_addr);
>>>>> +}
>>>>> +
>>>>> +void
>>>>> +rte_memarea_destroy(struct rte_memarea *ma)
>>>>> +{
>>>>> +    if (ma == NULL)
>>>>> +        return;
>>>>> +    memarea_free_area(ma);
>>>>> +    rte_free(ma);
>>>>> +}
>>>>> diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
>>>>> new file mode 100644
>>>>> index 0000000000..543cda4cac
>>>>> --- /dev/null
>>>>> +++ b/lib/memarea/rte_memarea.h
>>>>> @@ -0,0 +1,145 @@
>>>>> +/* SPDX-License-Identifier: BSD-3-Clause
>>>>> + * Copyright(c) 2022 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 default alignment size is RTE_CACHE_LINE_SIZE.
>>>>
>>>> This can be read as two things: the object size is aligned, or the 
>>>> start address is aligned.
>>>
>>> It means the start address align, will define more clear in v6.
>>>
>>>>
>>>>> + * - The memory region can be initialized from the following 
>>>>> memory sources:
>>>>> + *   1. RTE memory: e.g. invoke rte_malloc_socket to obtain.
>>>>
>>>> Remove "to obtain", or add "memory" after "obtain". Do you really 
>>>> mean "e.g.", and not "i.e."?
>>>
>>> will fix in v6.
>>>
>>>>
>>>>> + *   2. System API: e.g. invoke posix_memalign to obtain.
>>>>> + *   3. User provided address: it can be from extended memory as 
>>>>> long as it is
>>>>> + *      available. The address must be aligned to 
>>>>> RTE_CACHE_LINE_SIZE.
>>>>
>>>> What is extended memory?
>>>
>>> Like rte_extmen
>>>
>>>>
>>>>> + *   4) User provided memarea: it can be from another memarea. So 
>>>>> we can build
>>>>> + *      the following memory management structure:
>>>>> + *         \code{.unparsed}
>>>>> + *                           -------------
>>>>> + *                           | memarea-1 |
>>>>> + *                           -------------
>>>>> + *                                 |
>>>>> + *                                 v
>>>>> + *                  -------------------------------
>>>>> + *                  |               |             |
>>>>> + *                  v               v             v
>>>>> + *            -------------   -------------   ----------
>>>>> + *            | memarea-2 |   | memarea-3 |   | object |
>>>>> + *            -------------   -------------   ----------
>>>>> + *         \endcode
>>>>> + *      As shown above, the memarea-2/3 both create from 
>>>>> memarea-1's memory.
>>>>> + * - It provides refcnt feature which could be useful in 
>>>>> multi-reader scenario.
>>>>> + * - 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_RTE_MEMORY,
>>>>> +    /** Memory source comes from system API. */
>>>>> +    RTE_MEMAREA_SOURCE_SYSTEM_API,
>>>>> +    /** Memory source comes from user-provided address. */
>>>>> +    RTE_MEMAREA_SOURCE_USER_ADDR,
>>>>> +    /** Memory source comes from user-provided memarea. */
>>>>> +    RTE_MEMAREA_SOURCE_USER_MEMAREA,
>>>>> +};
>>>>> +
>>>>> +/**
>>>>> + * Memarea memory management algorithm.
>>>>> + */
>>>>> +enum rte_memarea_alg {
>>>>> +    /* The default management algorithm is a variant of the next fit
>>>>> +     * algorithm. It uses a free-list to apply for memory and uses an
>>>>> +     * element-list in ascending order of address to support merging
>>>>> +     * upon free.
>>>>> +     */
>>>>> +    RTE_MEMAREA_ALG_DEFAULT,
>>>>> +};
>>
>> Do you need to expose the algorithm/management scheme option in the 
>> public API, if there is only one implementation to choose from?
> 
> 
> Yes, we plan to support SLAB algorithm in future by adding a new alg.
> 
> 
>>
>> You can always have a rte_memarea_create_alg(/../) in the future, or 
>> just change the signature between ABI-breaking releases.
> 
> 
> I don't think add a new API is a good idea.
> 
> You can see that, we put all init parameter in one struct in this 
> design, and could extend function by adding new field .
> 
> 
>>
>>
>> Also, shouldn't the default algorithm have name? Rather than just 
>> DEFAULT. RTE_MEMAREA_ALG_NEXTFIT maybe.
> 
> 
> Yes, will fix in next version.
> 
> 
>>
>>>>> +
>>>>> +struct rte_memarea;
>>>>> +
>>>>> +struct rte_memarea_param {
>>>>
>>>> Does this struct need to be public?
>>>
>>> Yes, the rte_memarea_param contains create parameters for create 
>>> memarea which need be public.
>>>
>>
>> Why isn't the public API just a bunch of create-function parameters? 
>> Or, alternatively, you have an assortment of create functions, for 
>> different combinations of parameters. Then you don't have to think 
>> about having reserved bits or other kind of ABI-related issues due to 
>> the struct being public.
> 
> 
> These are two API design styles,and I prefer the one create-function 
> API style just it seem simple (by inner usage voice).
> 
> 

My reaction when I saw the test code, was that it was needlessly 
complicated to create a memarea. Intuitions vary, I suppose.

Anyway, the style you prefer is represented elsewhere in DPDK already, 
so I'm good with this.

>>
>>> And rte_memarea which pointer implementation struction which just a 
>>> declare here.
>>>
>>>>
>>>>> +    char name[RTE_MEMAREA_NAMESIZE]; /**< Name of memarea. */
>>>>> +    enum rte_memarea_source source;  /**< Memory source of 
>>>>> memarea. */
>>>>> +    enum rte_memarea_alg 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;
>>>>
>>>> Why not bool?
>>>
>>> Use bit field other than bool will provides more reserved field.
>>>
>>>>
>>>>> +    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_RTE_MEMORY.
>>>>> +         */
>>>>> +        int numa_socket;
>>>>> +        /** User provided address, this field is valid only when the
>>>>> +         * source is set to be RTE_MEMAREA_SOURCE_USER_ADDR.
>>>>> +         * Note: the provided address must align at least
>>>>> +         * RTE_CACHE_LINE_SIZE.
>>>>> +         */
>>>>> +        void *user_addr;
>>>>> +        /** User provided memarea, this field is valid only when the
>>>>> +         * source is set to be RTE_MEMAREA_SOURCE_USER_MEMAREA.
>>>>> +         */
>>>>> +        struct rte_memarea *user_memarea;
>>>>> +    };
>>>>> +};
>>>>> +
>>>>> +/**
>>>>> + * @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 */
>>>>> 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 c648f7d800..521a25d6c0 100644
>>>>> --- a/lib/meson.build
>>>>> +++ b/lib/meson.build
>>>>> @@ -42,6 +42,7 @@ libraries = [
>>>>>            'kni',
>>>>>            'latencystats',
>>>>>            'lpm',
>>>>> +        'memarea',
>>>>>            'member',
>>>>>            'pcapng',
>>>>>            'power',
>>>>
>>>> .
>>

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v8 1/9] memarea: introduce memarea library
  2022-10-11 12:17   ` [PATCH v8 1/9] memarea: " Chengwen Feng
@ 2022-10-11 15:58     ` Dmitry Kozlyuk
  2022-10-12  4:06       ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: Dmitry Kozlyuk @ 2022-10-11 15:58 UTC (permalink / raw)
  To: Chengwen Feng
  Cc: david.marchand, mb, anatoly.burakov, jerinjacobk, hofors, thomas, dev

2022-10-11 12:17 (UTC+0000), Chengwen Feng:
[...]
> diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
> new file mode 100644
> index 0000000000..85ad57145f
> --- /dev/null
> +++ b/doc/guides/prog_guide/memarea_lib.rst
> @@ -0,0 +1,39 @@
> +..  SPDX-License-Identifier: BSD-3-Clause
> +    Copyright(c) 2022 HiSilicon Limited
> +
> +Memarea Library
> +===============
> +
> +Introduction
> +------------
> +
> +The memarea library provides an allocator of variable-size objects, it is
> +oriented towards the application layer, which could provides 'region-based
> +memory management' function [1].
> +
> +The main features are as follows:
> +
> +* The allocated object aligned at ``RTE_CACHE_LINE_SIZE`` default.

Isn't this an implementation detail?
Stating it in the API description limits optimization opportunities.
Cache line alignment is good in many cases,
but it can also be a waste of space,
e.g. for a thread-unsafe region for small objects.
Can this limitation only (temporarily?) apply to user memory?
Or can the minimal alignment be a property of memarea?

> +
> +* The memory region can be initialized from the following memory sources:
> +  a) HEAP: e.g. invoke ``rte_malloc_socket``. b) LIBC: e.g. invoke
> +  posix_memalign to obtain. c) User memory: it can be from e.g. rte_extmem_xxx
> +  as long as it is available. d) Another memarea: it can be allocated from
> +  another memarea.

I think mentioning rte_extmem_xxx() is bogus
because user memory does not need to be registered with DPDK
(I understand it's an example, but still an unrelated reference).
Please format as a list.

> +
> +* It provides refcnt feature which could be useful in multi-reader scenario.
> +
> +* 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.
> +
> +Reference
> +---------
> +
> +[1] https://en.wikipedia.org/wiki/Region-based_memory_management
> diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
> index 2da8bc9661..f5a67cec7b 100644
> --- a/doc/guides/rel_notes/release_22_11.rst
> +++ b/doc/guides/rel_notes/release_22_11.rst
> @@ -63,6 +63,12 @@ New Features
>    In theory this implementation should work with any target based on
>    ``LoongArch`` ISA.
>  
> +* **Added memarea library.**
> +
> +  The memarea library is an allocator of variable-size objects, it is oriented
> +  towards the application layer, which could provides 'region-based memory
> +  management' function.

"which could provides" -> "providing"

> +
>  * **Added support for multiple mbuf pools per ethdev Rx queue.**
>  
>    The capability allows application to provide many mempools
[...]

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v8 3/9] memarea: support alloc/free/update-refcnt API
  2022-10-11 12:17   ` [PATCH v8 3/9] memarea: support alloc/free/update-refcnt API Chengwen Feng
@ 2022-10-11 15:58     ` Dmitry Kozlyuk
  2022-10-12  7:28       ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: Dmitry Kozlyuk @ 2022-10-11 15:58 UTC (permalink / raw)
  To: Chengwen Feng
  Cc: david.marchand, mb, anatoly.burakov, jerinjacobk, hofors, thomas, dev

2022-10-11 12:17 (UTC+0000), Chengwen Feng:
> This patch supports rte_memarea_alloc()/rte_memarea_free()/
> rte_memarea_update_refcnt() API.
> 
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> ---
>  doc/guides/prog_guide/memarea_lib.rst |  10 ++
>  lib/memarea/memarea_private.h         |   3 +
>  lib/memarea/rte_memarea.c             | 155 ++++++++++++++++++++++++++
>  lib/memarea/rte_memarea.h             |  56 ++++++++++
>  lib/memarea/version.map               |   3 +
>  5 files changed, 227 insertions(+)
> 
> diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
> index 85ad57145f..a9c58dc44d 100644
> --- a/doc/guides/prog_guide/memarea_lib.rst
> +++ b/doc/guides/prog_guide/memarea_lib.rst
> @@ -33,6 +33,16 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
>  
>  The ``rte_memarea_destroy()`` function is used to destroy a memarea.
>  
> +The ``rte_memarea_alloc()`` function is used to alloc one memory object from
> +the memarea.
> +
> +The ``rte_memarea_free()`` function is used to free one memory object which
> +allocated by ``rte_memarea_alloc()``.
> +
> +The ``rte_memarea_update_refcnt()`` function is used to update the memory
> +object's reference count, if the count reaches zero, the memory object will
> +be freed to memarea.
> +
>  Reference
>  ---------
>  
> diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
> index 59be9c1d00..f5accf2987 100644
> --- a/lib/memarea/memarea_private.h
> +++ b/lib/memarea/memarea_private.h
> @@ -28,6 +28,9 @@ struct rte_memarea {
>  	void                    *area_addr;
>  	struct memarea_elem_list elem_list;
>  	struct memarea_elem_list free_list;
> +
> +	uint64_t alloc_fails;
> +	uint64_t refcnt_check_fails;
>  } __rte_cache_aligned;
>  
>  #endif /* MEMAREA_PRIVATE_H */
> diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
> index 85975029e2..8ad1c0acb5 100644
> --- a/lib/memarea/rte_memarea.c
> +++ b/lib/memarea/rte_memarea.c
> @@ -4,6 +4,7 @@
>  
>  #include <stdio.h>
>  #include <stdlib.h>
> +#include <sys/queue.h>
>  
>  #include <rte_common.h>
>  #include <rte_log.h>
> @@ -94,6 +95,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
>  		ptr = memarea_alloc_from_libc(init->total_sz);
>  	else if (init->source == RTE_MEMAREA_SOURCE_USER)
>  		ptr = init->user_addr;
> +	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
> +		ptr = rte_memarea_alloc(init->src_memarea, init->total_sz, 0);
>  
>  	if (ptr == NULL)
>  		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
> @@ -146,6 +149,8 @@ memarea_free_area(struct rte_memarea *ma)
>  		rte_free(ma->area_addr);
>  	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
>  		free(ma->area_addr);
> +	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
> +		rte_memarea_free(ma->init.src_memarea, ma->area_addr);
>  }
>  
>  void
> @@ -156,3 +161,153 @@ rte_memarea_destroy(struct rte_memarea *ma)
>  	memarea_free_area(ma);
>  	rte_free(ma);
>  }
> +
> +static inline void
> +memarea_lock(struct rte_memarea *ma)
> +{
> +	if (ma->init.mt_safe)
> +		rte_spinlock_lock(&ma->lock);
> +}
> +
> +static inline void
> +memarea_unlock(struct rte_memarea *ma)
> +{
> +	if (ma->init.mt_safe)
> +		rte_spinlock_unlock(&ma->lock);
> +}
> +
> +static inline bool
> +memarea_whether_add_node(size_t free_size, size_t need_size)
> +{
> +	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
> +	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
> +}
> +
> +static inline void
> +memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
> +{
> +	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
> +	struct memarea_elem *new_elem;
> +	new_elem = (struct memarea_elem *)RTE_PTR_ADD(elem, sizeof(struct memarea_elem) +
> +							    align_size);
> +	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
> +	new_elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
> +	new_elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
> +	new_elem->refcnt = 0;
> +	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
> +	TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
> +	elem->size = align_size;
> +}
> +
> +void *
> +rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
> +{
> +	struct memarea_elem *elem;
> +	void *ptr = NULL;
> +
> +	if (unlikely(ma == NULL || size == 0))
> +		return NULL;
> +
> +	memarea_lock(ma);
> +	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
> +		if (unlikely(elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC))
> +			break;
> +		if (elem->size < size)
> +			continue;
> +		if (memarea_whether_add_node(elem->size, size))
> +			memarea_add_node(ma, elem, size);
> +		elem->magic = MEMAREA_ALLOCATED_ELEM_MAGIC;
> +		elem->cookie = cookie;
> +		elem->refcnt = 1;
> +		TAILQ_REMOVE(&ma->free_list, elem, free_node);
> +		ptr = RTE_PTR_ADD(elem, sizeof(struct memarea_elem));
> +		break;
> +	}
> +	if (unlikely(ptr == NULL))
> +		ma->alloc_fails++;
> +	memarea_unlock(ma);
> +
> +	return ptr;
> +}
> +
> +void
> +rte_memarea_free(struct rte_memarea *ma, void *ptr)
> +{
> +	rte_memarea_update_refcnt(ma, ptr, -1);
> +}
> +
> +static inline void
> +memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
> +		   struct memarea_elem *next, bool del_next_from_free,
> +		   bool add_curr_to_free)
> +{
> +	curr->size += next->size + sizeof(struct memarea_elem);
> +	next->size = 0;
> +	next->magic = 0;
> +	next->cookie = 0;
> +	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
> +	if (del_next_from_free)
> +		TAILQ_REMOVE(&ma->free_list, next, free_node);
> +	if (add_curr_to_free) {
> +		curr->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
> +		curr->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
> +		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
> +	}
> +}
> +
> +static inline void
> +memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
> +{
> +	struct memarea_elem *prev, *next;
> +	bool merged = false;
> +	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
> +	next = TAILQ_NEXT(elem, elem_node);
> +	if (prev != NULL && prev->refcnt == 0) {
> +		memarea_merge_node(ma, prev, elem, false, false);
> +		elem = prev;
> +		merged = true;
> +	}
> +	if (next != NULL && next->refcnt == 0) {
> +		memarea_merge_node(ma, elem, next, true, !merged);
> +		merged = true;
> +	}
> +	if (!merged) {
> +		elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
> +		elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
> +		TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
> +	}
> +}
> +
> +void
> +rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
> +{
> +	struct memarea_elem *elem = (struct memarea_elem *)RTE_PTR_SUB(ptr,
> +							    sizeof(struct memarea_elem));
> +
> +	if (unlikely(ma == NULL || ptr == NULL))
> +		return;

This check is probably not needed:
the user is only supposed to change refcnt of a valid object.
Since rte_memarea_free(ma, NULL) must be a no-op, the check should be there.

> +
> +	memarea_lock(ma);
> +	if (unlikely(elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC)) {
> +		RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail!\n",
> +			ma->init.name, elem->magic);

Element pointer might be useful here,
because cookie will likely not be unique for each object.

> +		memarea_unlock(ma);
> +		return;
> +	}
> +
> +	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
> +		RTE_LOG(ERR, MEMAREA,
> +			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
> +			ma->init.name, elem->cookie, elem->refcnt, value);

Same as above.

> +		ma->refcnt_check_fails++;
> +		if (elem->refcnt > 0)
> +			elem->refcnt += value;

This may change refcnt of some unrelated block allocated concurrently,
producing another bug on top of the bug that has lead to this branch
(the case is different from the race described below).

> +		memarea_unlock(ma);
> +		return;
> +	}
> +
> +	elem->refcnt += value;
> +	if (elem->refcnt == 0)
> +		memarea_free_elem(ma, elem);
> +	memarea_unlock(ma);
> +}

This is not thread-safe:

1. Thread A wants to decrement refnct and takes the lock.
2. Thread B wants to increment refcnt and waits for the lock.
3. Thread C wants to allocate and waits for the lock.
4. Thread A decrements refcnt, frees the object, and releases the lock.
5. Thread C takes the lock, allocates the same element, and releases the lock.
6. Thread B takes the lock and increments refcnt of the new element allocated
by thread C and not the old element it had a pointer to in the beginning.

Probably some memarea generation counter could solve this.

I still tend to think that reference counting is too usage-specific
to be a part of a generic memory management library,
though others don't seem to object.

> diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
> index ac2d63a4d3..f866fa7b67 100644
> --- a/lib/memarea/rte_memarea.h
> +++ b/lib/memarea/rte_memarea.h
> @@ -129,6 +129,62 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
>  __rte_experimental
>  void rte_memarea_destroy(struct rte_memarea *ma);
>  
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Allocate memory from memarea.
> + *
> + * Allocate one memory object from the memarea.
> + *
> + * @param ma
> + *   The pointer of memarea.
> + * @param size
> + *   The memory size to be allocated.
> + * @param cookie
> + *   User-provided footprint which could used to debug memory leak.
> + *
> + * @return
> + *   Non-NULL on success. Otherwise NULL is returned.

Also NULL when size == 0 (OK, like malloc) or when ma == NULL (not obvious).

> + */
> +__rte_experimental
> +void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Free memory to memarea.
> + *
> + * Free one memory object to the memarea.

Not really if refcnt > 1.
Should it assert somehow that refcnt <= 1?

The second sentence adds nothing to the first.

> + *
> + * @param ma
> + *   The pointer of memarea.
> + * @param ptr
> + *   The pointer of memory object which need be freed.
> + */
> +__rte_experimental
> +void rte_memarea_free(struct rte_memarea *ma, void *ptr);

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v8 7/9] memarea: support backup memory mechanism
  2022-10-11 12:17   ` [PATCH v8 7/9] memarea: support backup memory mechanism Chengwen Feng
@ 2022-10-11 15:58     ` Dmitry Kozlyuk
  2022-10-11 20:26       ` Mattias Rönnblom
  2022-10-12  7:57       ` fengchengwen
  0 siblings, 2 replies; 222+ messages in thread
From: Dmitry Kozlyuk @ 2022-10-11 15:58 UTC (permalink / raw)
  To: Chengwen Feng
  Cc: david.marchand, mb, anatoly.burakov, jerinjacobk, hofors, thomas, dev

2022-10-11 12:17 (UTC+0000), Chengwen Feng:
> This patch adds a memarea backup mechanism, where an allocation request
> which cannot be met by the current memarea is deferred to its backup
> memarea.

This is a controversial feature.

1. It violates memarea property of freeing all allocated objects
   at once when the memarea itself is destroyed. Objects allocated
   in the backup memarea through the destroyed one will remain.

2. If there was an API to check that the object belongs to a memarea
   (the check from rte_memarea_update_refcnt() in this patch),
   it would be trivial to implement this feature on top of memarea API.

Nit: "Deferred" is about time -> "forwarded", "delegated", or "handled over".

A general note about this series.
IMO, libraries should have limited scope and allow composition
rather than accumulate features and control their use via options.
The core idea of memarea is an allocator within a memory region,
a fast one and with low overhead, usable to free all objects at once.

This is orthogonal to the question from where the memory comes from.
HEAP and LIBC sources could be built on top of USER source,
which means that the concept of source is less relevant.
Backup mechanism could instead be a way to add memory to the area,
in which case HEAP and LIBC memarea would also be expandable.
Memarea API could be defined as a structure with callbacks,
and different types of memarea could be combined,
for example, interlocked memarea on top of expandable memarea on top of
memarea with a particular memory management algorithm.

I'm not saying we should immediately build all this complexity.
On the contrary, I would merge the basic things first,
then try to _stack_ new features on top,
then look if interfaces emerge that can be used for composition.

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v8 9/9] app/test: add memarea to malloc-perf-autotest
  2022-10-11 12:17   ` [PATCH v8 9/9] app/test: add memarea to malloc-perf-autotest Chengwen Feng
@ 2022-10-11 15:58     ` Dmitry Kozlyuk
  2022-10-12  8:03       ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: Dmitry Kozlyuk @ 2022-10-11 15:58 UTC (permalink / raw)
  To: Chengwen Feng
  Cc: david.marchand, mb, anatoly.burakov, jerinjacobk, hofors, thomas, dev

2022-10-11 12:17 (UTC+0000), Chengwen Feng:
> This patch adds memarea to malloc_perf_autotest.
> 
> Test platform: Kunpeng920
> Test command: dpdk-test -a 0000:7d:00.3 -l 10-12
> Test result:
> USER1: Performance: rte_memarea
> USER1:    Size (B)   Runs  Alloc (us) Free (us) Total (us)  memset (us)
> USER1:          64  10000        0.03      0.03       0.06         0.01
> USER1:         128  10000        0.02      0.03       0.05         0.01
> USER1:        1024  10000        0.03      0.05       0.07         0.20
> USER1:        4096  10000        0.03      0.05       0.07         0.34
> USER1:       65536  10000        0.10      0.08       0.18         2.14
> USER1:     1048576    644        0.10      0.04       0.14        29.07
> USER1:     2097152    322        0.10      0.04       0.14        57.50
> USER1:     4194304    161        0.12      0.04       0.15       114.50
> USER1:    16777216     40        0.11      0.04       0.15       456.09
> USER1:  1073741824 Interrupted: out of memory. [1]
> 
> Compared with rte_malloc:
> USER1: Performance: rte_malloc
> USER1:    Size (B)   Runs  Alloc (us) Free (us) Total (us)  memset (us)
> USER1:          64  10000        0.14      0.07       0.21         0.01
> USER1:         128  10000        0.10      0.05       0.15         0.01
> USER1:        1024  10000        0.11      0.18       0.29         0.21
> USER1:        4096  10000        0.13      0.39       0.53         0.35
> USER1:       65536  10000        0.17      2.27       2.44         2.15
> USER1:     1048576  10000       37.21     71.63     108.84        29.08
> USER1:     2097152  10000     8831.15    160.02    8991.17        63.52
> USER1:     4194304  10000    47131.88    413.75   47545.62       173.79
> USER1:    16777216   4221   119604.60   2209.73  121814.34       964.42
> USER1:  1073741824     31   335058.32 223369.31  558427.63     62440.87
> 
> [1] The total-size of the memarea is restricted to avoid creation
> failed.

This is not a fair comparison:
rte_malloc time includes obtaining memory from the system.
I think that memarea should have a dedicated benchmark,
because eventually it will be interesting to compare memarea
with different sources and algorithms.
It will be also possible to add DPDK allocator to the comparison
by running it for an isolated heap that doesn't grow.
(In some distant future it would be cool to make DPDK allocator pluggable!)
Some shared code between this benchmark and the new one can be factored out.

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v8 7/9] memarea: support backup memory mechanism
  2022-10-11 15:58     ` Dmitry Kozlyuk
@ 2022-10-11 20:26       ` Mattias Rönnblom
  2022-10-12  8:23         ` fengchengwen
  2022-10-12  7:57       ` fengchengwen
  1 sibling, 1 reply; 222+ messages in thread
From: Mattias Rönnblom @ 2022-10-11 20:26 UTC (permalink / raw)
  To: Dmitry Kozlyuk, Chengwen Feng
  Cc: david.marchand, mb, anatoly.burakov, jerinjacobk, thomas, dev

On 2022-10-11 17:58, Dmitry Kozlyuk wrote:
> 2022-10-11 12:17 (UTC+0000), Chengwen Feng:
>> This patch adds a memarea backup mechanism, where an allocation request
>> which cannot be met by the current memarea is deferred to its backup
>> memarea.
> 
> This is a controversial feature.
> 
> 1. It violates memarea property of freeing all allocated objects
>     at once when the memarea itself is destroyed. Objects allocated
>     in the backup memarea through the destroyed one will remain.
> 
> 2. If there was an API to check that the object belongs to a memarea
>     (the check from rte_memarea_update_refcnt() in this patch),
>     it would be trivial to implement this feature on top of memarea API.
> 
> Nit: "Deferred" is about time -> "forwarded", "delegated", or "handled over".
> 
> A general note about this series.
> IMO, libraries should have limited scope and allow composition
> rather than accumulate features and control their use via options.
> The core idea of memarea is an allocator within a memory region,
> a fast one and with low overhead, usable to free all objects at once.
> 

What's a typical use case for a memory region? In a packet processing 
context.

The ability to instantiate a variable number of heaps/regions seems 
useful, although it's not clear to me if the application should order 
that to happen on a per-lcore basis, on a per-NUMA node basis, a 
per-<some domain object>-basis, or something else entirely.

It seems to me that DPDK is lacking a variable-size memory allocator 
which is efficient and safe to use from lcore threads. My impression is 
that glibc malloc() and rte_malloc() are too slow for the packet 
processing threads, and involves code paths taking locks shared with 
non-EAL threads.

> This is orthogonal to the question from where the memory comes from.
> HEAP and LIBC sources could be built on top of USER source,
> which means that the concept of source is less relevant.
> Backup mechanism could instead be a way to add memory to the area,
> in which case HEAP and LIBC memarea would also be expandable.
> Memarea API could be defined as a structure with callbacks,
> and different types of memarea could be combined,
> for example, interlocked memarea on top of expandable memarea on top of
> memarea with a particular memory management algorithm.
> 
> I'm not saying we should immediately build all this complexity.

The part with implementing runtime polymorphism using a struct with 
function pointers, instead of the enum+switch-based-type-test approach, 
doesn't sound like something that would add complexity. Rather the opposite.

Also, having a clear-cut separation of concern between 
the-thing-that-allocates-and-frees-the-region and the region-internal 
memory manager (what's called an algorithm in this patchset) also seems 
like something that would simplify the code.

> On the contrary, I would merge the basic things first,
> then try to _stack_ new features on top,
> then look if interfaces emerge that can be used for composition.

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v8 1/9] memarea: introduce memarea library
  2022-10-11 15:58     ` Dmitry Kozlyuk
@ 2022-10-12  4:06       ` fengchengwen
  2022-10-13 10:45         ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: fengchengwen @ 2022-10-12  4:06 UTC (permalink / raw)
  To: Dmitry Kozlyuk
  Cc: david.marchand, mb, anatoly.burakov, jerinjacobk, hofors, thomas, dev

Hi Dmitry,

On 2022/10/11 23:58, Dmitry Kozlyuk wrote:
> 2022-10-11 12:17 (UTC+0000), Chengwen Feng:
> [...]
>> diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
>> new file mode 100644
>> index 0000000000..85ad57145f
>> --- /dev/null
>> +++ b/doc/guides/prog_guide/memarea_lib.rst
>> @@ -0,0 +1,39 @@
>> +..  SPDX-License-Identifier: BSD-3-Clause
>> +    Copyright(c) 2022 HiSilicon Limited
>> +
>> +Memarea Library
>> +===============
>> +
>> +Introduction
>> +------------
>> +
>> +The memarea library provides an allocator of variable-size objects, it is
>> +oriented towards the application layer, which could provides 'region-based
>> +memory management' function [1].
>> +
>> +The main features are as follows:
>> +
>> +* The allocated object aligned at ``RTE_CACHE_LINE_SIZE`` default.
> 
> Isn't this an implementation detail?
> Stating it in the API description limits optimization opportunities.
> Cache line alignment is good in many cases,
> but it can also be a waste of space,
> e.g. for a thread-unsafe region for small objects.
> Can this limitation only (temporarily?) apply to user memory?
> Or can the minimal alignment be a property of memarea?

You are right, to be more general, I'll add the align parameter to rte_memarea_alloc instead of stating it here.

> 
>> +
>> +* The memory region can be initialized from the following memory sources:
>> +  a) HEAP: e.g. invoke ``rte_malloc_socket``. b) LIBC: e.g. invoke
>> +  posix_memalign to obtain. c) User memory: it can be from e.g. rte_extmem_xxx
>> +  as long as it is available. d) Another memarea: it can be allocated from
>> +  another memarea.
> 
> I think mentioning rte_extmem_xxx() is bogus
> because user memory does not need to be registered with DPDK
> (I understand it's an example, but still an unrelated reference).
> Please format as a list.

okay

> 
>> +
>> +* It provides refcnt feature which could be useful in multi-reader scenario.
>> +
>> +* 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.
>> +
>> +Reference
>> +---------
>> +
>> +[1] https://en.wikipedia.org/wiki/Region-based_memory_management
>> diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
>> index 2da8bc9661..f5a67cec7b 100644
>> --- a/doc/guides/rel_notes/release_22_11.rst
>> +++ b/doc/guides/rel_notes/release_22_11.rst
>> @@ -63,6 +63,12 @@ New Features
>>    In theory this implementation should work with any target based on
>>    ``LoongArch`` ISA.
>>  
>> +* **Added memarea library.**
>> +
>> +  The memarea library is an allocator of variable-size objects, it is oriented
>> +  towards the application layer, which could provides 'region-based memory
>> +  management' function.
> 
> "which could provides" -> "providing"

okay

> 
>> +
>>  * **Added support for multiple mbuf pools per ethdev Rx queue.**
>>  
>>    The capability allows application to provide many mempools
> [...]
> .
> 

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v8 3/9] memarea: support alloc/free/update-refcnt API
  2022-10-11 15:58     ` Dmitry Kozlyuk
@ 2022-10-12  7:28       ` fengchengwen
  0 siblings, 0 replies; 222+ messages in thread
From: fengchengwen @ 2022-10-12  7:28 UTC (permalink / raw)
  To: Dmitry Kozlyuk
  Cc: david.marchand, mb, anatoly.burakov, jerinjacobk, hofors, thomas, dev

Hi Dmitry,

On 2022/10/11 23:58, Dmitry Kozlyuk wrote:
> 2022-10-11 12:17 (UTC+0000), Chengwen Feng:
>> This patch supports rte_memarea_alloc()/rte_memarea_free()/
>> rte_memarea_update_refcnt() API.
>>

...

>> +
>> +void
>> +rte_memarea_update_refcnt(struct rte_memarea *ma, void *ptr, int16_t value)
>> +{
>> +	struct memarea_elem *elem = (struct memarea_elem *)RTE_PTR_SUB(ptr,
>> +							    sizeof(struct memarea_elem));
>> +
>> +	if (unlikely(ma == NULL || ptr == NULL))
>> +		return;
> 
> This check is probably not needed:
> the user is only supposed to change refcnt of a valid object.
> Since rte_memarea_free(ma, NULL) must be a no-op, the check should be there.

okay

> 
>> +
>> +	memarea_lock(ma);
>> +	if (unlikely(elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC)) {
>> +		RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail!\n",
>> +			ma->init.name, elem->magic);
> 
> Element pointer might be useful here,
> because cookie will likely not be unique for each object.

RTE_LOG pointer may lead to security risks.
I tend to keep the status quo.

> 
>> +		memarea_unlock(ma);
>> +		return;
>> +	}
>> +
>> +	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
>> +		RTE_LOG(ERR, MEMAREA,
>> +			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
>> +			ma->init.name, elem->cookie, elem->refcnt, value);
> 
> Same as above.
> 
>> +		ma->refcnt_check_fails++;
>> +		if (elem->refcnt > 0)
>> +			elem->refcnt += value;
> 
> This may change refcnt of some unrelated block allocated concurrently,
> producing another bug on top of the bug that has lead to this branch
> (the case is different from the race described below).
> 
>> +		memarea_unlock(ma);
>> +		return;
>> +	}
>> +
>> +	elem->refcnt += value;
>> +	if (elem->refcnt == 0)
>> +		memarea_free_elem(ma, elem);
>> +	memarea_unlock(ma);
>> +}
> 
> This is not thread-safe:
> 
> 1. Thread A wants to decrement refnct and takes the lock.
> 2. Thread B wants to increment refcnt and waits for the lock.

The refcnt must at least 2 because two threads hold the object.

> 3. Thread C wants to allocate and waits for the lock.
> 4. Thread A decrements refcnt, frees the object, and releases the lock.

The refcnt will updated to be 1, and the object will not freed.

> 5. Thread C takes the lock, allocates the same element, and releases the lock> 6. Thread B takes the lock and increments refcnt of the new element allocated
> by thread C and not the old element it had a pointer to in the beginning.

Because object's refcnt at least 1, so the increments refcnt will success.

The refcnt just like mbuf's refcnt, If app wants to send one object to multiple entity
(rings or other), it should updated refcnt by increment (multiple entity num - 1) and
then send.

It work well when keep the above rule.

> 
> Probably some memarea generation counter could solve this.
> 
> I still tend to think that reference counting is too usage-specific
> to be a part of a generic memory management library,
> though others don't seem to object.
> 
>> diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
>> index ac2d63a4d3..f866fa7b67 100644
>> --- a/lib/memarea/rte_memarea.h
>> +++ b/lib/memarea/rte_memarea.h
>> @@ -129,6 +129,62 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
>>  __rte_experimental
>>  void rte_memarea_destroy(struct rte_memarea *ma);
>>  
>> +/**
>> + * @warning
>> + * @b EXPERIMENTAL: this API may change without prior notice.
>> + *
>> + * Allocate memory from memarea.
>> + *
>> + * Allocate one memory object from the memarea.
>> + *
>> + * @param ma
>> + *   The pointer of memarea.
>> + * @param size
>> + *   The memory size to be allocated.
>> + * @param cookie
>> + *   User-provided footprint which could used to debug memory leak.
>> + *
>> + * @return
>> + *   Non-NULL on success. Otherwise NULL is returned.
> 
> Also NULL when size == 0 (OK, like malloc) or when ma == NULL (not obvious).

ok

> 
>> + */
>> +__rte_experimental
>> +void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
>> +
>> +/**
>> + * @warning
>> + * @b EXPERIMENTAL: this API may change without prior notice.
>> + *
>> + * Free memory to memarea.
>> + *
>> + * Free one memory object to the memarea.
> 
> Not really if refcnt > 1.
> Should it assert somehow that refcnt <= 1?
> 
> The second sentence adds nothing to the first.

will add more description.

> 
>> + *
>> + * @param ma
>> + *   The pointer of memarea.
>> + * @param ptr
>> + *   The pointer of memory object which need be freed.
>> + */
>> +__rte_experimental
>> +void rte_memarea_free(struct rte_memarea *ma, void *ptr);
> .
> 

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v8 7/9] memarea: support backup memory mechanism
  2022-10-11 15:58     ` Dmitry Kozlyuk
  2022-10-11 20:26       ` Mattias Rönnblom
@ 2022-10-12  7:57       ` fengchengwen
  1 sibling, 0 replies; 222+ messages in thread
From: fengchengwen @ 2022-10-12  7:57 UTC (permalink / raw)
  To: Dmitry Kozlyuk
  Cc: david.marchand, mb, anatoly.burakov, jerinjacobk, hofors, thomas, dev

Hi Dmitry,

On 2022/10/11 23:58, Dmitry Kozlyuk wrote:
> 2022-10-11 12:17 (UTC+0000), Chengwen Feng:
>> This patch adds a memarea backup mechanism, where an allocation request
>> which cannot be met by the current memarea is deferred to its backup
>> memarea.
> 
> This is a controversial feature.
> 
> 1. It violates memarea property of freeing all allocated objects
>    at once when the memarea itself is destroyed. Objects allocated
>    in the backup memarea through the destroyed one will remain.
> 
> 2. If there was an API to check that the object belongs to a memarea
>    (the check from rte_memarea_update_refcnt() in this patch),
>    it would be trivial to implement this feature on top of memarea API.

This patch add 'struct memarea *owner' in each object's meta data, and it was
used to free which allocated from backup memarea. So this problem will not exist.

> 
> Nit: "Deferred" is about time -> "forwarded", "delegated", or "handled over".

ok

> 
> A general note about this series.
> IMO, libraries should have limited scope and allow composition
> rather than accumulate features and control their use via options.
> The core idea of memarea is an allocator within a memory region,
> a fast one and with low overhead, usable to free all objects at once.
> 
> This is orthogonal to the question from where the memory comes from.
> HEAP and LIBC sources could be built on top of USER source,
> which means that the concept of source is less relevant.
> Backup mechanism could instead be a way to add memory to the area,
> in which case HEAP and LIBC memarea would also be expandable.
> Memarea API could be defined as a structure with callbacks,
> and different types of memarea could be combined,
> for example, interlocked memarea on top of expandable memarea on top of
> memarea with a particular memory management algorithm.
> 
> I'm not saying we should immediately build all this complexity.
> On the contrary, I would merge the basic things first,
> then try to _stack_ new features on top,
> then look if interfaces emerge that can be used for composition.

Agree, I will drop this feature in next version.

> .
> 

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v8 9/9] app/test: add memarea to malloc-perf-autotest
  2022-10-11 15:58     ` Dmitry Kozlyuk
@ 2022-10-12  8:03       ` fengchengwen
  0 siblings, 0 replies; 222+ messages in thread
From: fengchengwen @ 2022-10-12  8:03 UTC (permalink / raw)
  To: Dmitry Kozlyuk
  Cc: david.marchand, mb, anatoly.burakov, jerinjacobk, hofors, thomas, dev

Hi Dmitry,

On 2022/10/11 23:58, Dmitry Kozlyuk wrote:
> 2022-10-11 12:17 (UTC+0000), Chengwen Feng:
>> This patch adds memarea to malloc_perf_autotest.
>>
>> Test platform: Kunpeng920
>> Test command: dpdk-test -a 0000:7d:00.3 -l 10-12
>> Test result:
>> USER1: Performance: rte_memarea
>> USER1:    Size (B)   Runs  Alloc (us) Free (us) Total (us)  memset (us)
>> USER1:          64  10000        0.03      0.03       0.06         0.01
>> USER1:         128  10000        0.02      0.03       0.05         0.01
>> USER1:        1024  10000        0.03      0.05       0.07         0.20
>> USER1:        4096  10000        0.03      0.05       0.07         0.34
>> USER1:       65536  10000        0.10      0.08       0.18         2.14
>> USER1:     1048576    644        0.10      0.04       0.14        29.07
>> USER1:     2097152    322        0.10      0.04       0.14        57.50
>> USER1:     4194304    161        0.12      0.04       0.15       114.50
>> USER1:    16777216     40        0.11      0.04       0.15       456.09
>> USER1:  1073741824 Interrupted: out of memory. [1]
>>
>> Compared with rte_malloc:
>> USER1: Performance: rte_malloc
>> USER1:    Size (B)   Runs  Alloc (us) Free (us) Total (us)  memset (us)
>> USER1:          64  10000        0.14      0.07       0.21         0.01
>> USER1:         128  10000        0.10      0.05       0.15         0.01
>> USER1:        1024  10000        0.11      0.18       0.29         0.21
>> USER1:        4096  10000        0.13      0.39       0.53         0.35
>> USER1:       65536  10000        0.17      2.27       2.44         2.15
>> USER1:     1048576  10000       37.21     71.63     108.84        29.08
>> USER1:     2097152  10000     8831.15    160.02    8991.17        63.52
>> USER1:     4194304  10000    47131.88    413.75   47545.62       173.79
>> USER1:    16777216   4221   119604.60   2209.73  121814.34       964.42
>> USER1:  1073741824     31   335058.32 223369.31  558427.63     62440.87
>>
>> [1] The total-size of the memarea is restricted to avoid creation
>> failed.
> 
> This is not a fair comparison:
> rte_malloc time includes obtaining memory from the system.

Yes, but I want to keep this patch, at least we know the different.

> I think that memarea should have a dedicated benchmark,
> because eventually it will be interesting to compare memarea
> with different sources and algorithms.

It may take a long time to reach a benchmark that everyone agrees with.
I will try after this patch set upstreamed.

> It will be also possible to add DPDK allocator to the comparison
> by running it for an isolated heap that doesn't grow.
> (In some distant future it would be cool to make DPDK allocator pluggable!)
> Some shared code between this benchmark and the new one can be factored out.
> .
> 

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v8 7/9] memarea: support backup memory mechanism
  2022-10-11 20:26       ` Mattias Rönnblom
@ 2022-10-12  8:23         ` fengchengwen
  0 siblings, 0 replies; 222+ messages in thread
From: fengchengwen @ 2022-10-12  8:23 UTC (permalink / raw)
  To: Mattias Rönnblom, Dmitry Kozlyuk
  Cc: david.marchand, mb, anatoly.burakov, jerinjacobk, thomas, dev

Hi Mattias,

On 2022/10/12 4:26, Mattias Rönnblom wrote:
> On 2022-10-11 17:58, Dmitry Kozlyuk wrote:
>> 2022-10-11 12:17 (UTC+0000), Chengwen Feng:
>>> This patch adds a memarea backup mechanism, where an allocation request
>>> which cannot be met by the current memarea is deferred to its backup
>>> memarea.
>>
>> This is a controversial feature.
>>
>> 1. It violates memarea property of freeing all allocated objects
>>     at once when the memarea itself is destroyed. Objects allocated
>>     in the backup memarea through the destroyed one will remain.
>>
>> 2. If there was an API to check that the object belongs to a memarea
>>     (the check from rte_memarea_update_refcnt() in this patch),
>>     it would be trivial to implement this feature on top of memarea API.
>>
>> Nit: "Deferred" is about time -> "forwarded", "delegated", or "handled over".
>>
>> A general note about this series.
>> IMO, libraries should have limited scope and allow composition
>> rather than accumulate features and control their use via options.
>> The core idea of memarea is an allocator within a memory region,
>> a fast one and with low overhead, usable to free all objects at once.
>>
> 
> What's a typical use case for a memory region? In a packet processing context.

we used it in video system:

   udp-packets  ->  splitter ->  stream-1-reorder  -> stream-1-decoder --
                             |                                          |
                             |                                          |--> compose-picture  -> picture-encoder -> udp-packets
                             |                                          |
                             ->  stream-2-reorder  -> stream-2-decoder --

each stream-decoder use a dedicated memarea which have following advantage:
1. there will no global lock which like rte_malloc
2. memory objects leakage will only impact the decoder, it will not impact global system.
3. simple the programmer, only destroy the memarea after the session finished.

As you see, this is a different idea of memory optimization, in pktmbuf_pool, it use
prealloc + per-lcore cache, it readuces lock conficts by lcore cache, but didn't fix
memory-leakage's impact.

> 
> The ability to instantiate a variable number of heaps/regions seems useful, although it's not clear to me if the application should order that to happen on a per-lcore basis, on a per-NUMA node basis, a per-<some domain object>-basis, or something else entirely.
> 
> It seems to me that DPDK is lacking a variable-size memory allocator which is efficient and safe to use from lcore threads. My impression is that glibc malloc() and rte_malloc() are too slow for the packet processing threads, and involves code paths taking locks shared with non-EAL threads.
> 
>> This is orthogonal to the question from where the memory comes from.
>> HEAP and LIBC sources could be built on top of USER source,
>> which means that the concept of source is less relevant.
>> Backup mechanism could instead be a way to add memory to the area,
>> in which case HEAP and LIBC memarea would also be expandable.
>> Memarea API could be defined as a structure with callbacks,
>> and different types of memarea could be combined,
>> for example, interlocked memarea on top of expandable memarea on top of
>> memarea with a particular memory management algorithm.
>>
>> I'm not saying we should immediately build all this complexity.
> 
> The part with implementing runtime polymorphism using a struct with function pointers, instead of the enum+switch-based-type-test approach, doesn't sound like something that would add complexity. Rather the opposite.
> 
> Also, having a clear-cut separation of concern between the-thing-that-allocates-and-frees-the-region and the region-internal memory manager (what's called an algorithm in this patchset) also seems like something that would simplify the code.
> 
>> On the contrary, I would merge the basic things first,
>> then try to _stack_ new features on top,
>> then look if interfaces emerge that can be used for composition.
> 
> .

^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v9 0/7] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (12 preceding siblings ...)
  2022-10-11 12:17 ` [PATCH v8 0/9] " Chengwen Feng
@ 2022-10-12 10:47 ` Chengwen Feng
  2022-10-12 10:47   ` [PATCH v9 1/7] memarea: " Chengwen Feng
                     ` (6 more replies)
  2022-10-17  3:40 ` [PATCH v10 0/7] introduce memarea library Chengwen Feng
                   ` (9 subsequent siblings)
  23 siblings, 7 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-12 10:47 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- 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 from another memarea.

- It provides refcnt feature which could be useful in multi-reader
  scenario.

- It supports MT-safe as long as it's specified at creation time.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>

Chengwen Feng (7):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc/free/refcnt-update API
  test/memarea: support alloc/free/refcnt-update test
  memarea: support dump API
  test/memarea: support dump test
  app/test: add memarea to malloc-perf-autotest

---
v9:
* address Dmitry's comments.
* drop features of SOURCE_USER and backup memarea mechanism.
* rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
  to keep with rte_mbuf_refcnt_update name style.
* fix memarea perftest compile failed at windows platform.
* fix spell warning.
v8:
* address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
* small feature patches are combined.
* enhanced backup memory mechanism.
* add memarea to malloc-perf-autotest.
* other tiny naming optimize.
v7:
* repost patches as there are spread over different series in patchwork.
v6:
* address Mattias's comments.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_malloc_perf.c            |  75 +++++
 app/test/test_memarea.c                | 320 ++++++++++++++++++++
 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  |  52 ++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  36 +++
 lib/memarea/meson.build                |  16 +
 lib/memarea/rte_memarea.c              | 398 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 219 ++++++++++++++
 lib/memarea/version.map                |  16 +
 lib/meson.build                        |   1 +
 17 files changed, 1153 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v9 1/7] memarea: introduce memarea library
  2022-10-12 10:47 ` [PATCH v9 0/7] introduce memarea library Chengwen Feng
@ 2022-10-12 10:47   ` Chengwen Feng
  2022-10-12 10:48   ` [PATCH v9 2/7] test/memarea: support memarea test Chengwen Feng
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-12 10:47 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

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>
---
 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  |  39 +++++++
 doc/guides/rel_notes/release_22_11.rst |   6 ++
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  33 ++++++
 lib/memarea/meson.build                |  16 +++
 lib/memarea/rte_memarea.c              | 143 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 124 +++++++++++++++++++++
 lib/memarea/version.map                |  12 +++
 lib/meson.build                        |   1 +
 14 files changed, 385 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 2bd4a55f1b..3d8d4c2dbe 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1551,6 +1551,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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index de488c7abf..24456604f8 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -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),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index f0886c3bd1..8334ebcbd6 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -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 \
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..d032b24ce9
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -0,0 +1,39 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2022 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 provides refcnt feature which could be useful in multi-reader scenario.
+
+* 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.
+
+Reference
+---------
+
+[1] https://en.wikipedia.org/wiki/Region-based_memory_management
diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
index 2da8bc9661..f8e3a23db7 100644
--- a/doc/guides/rel_notes/release_22_11.rst
+++ b/doc/guides/rel_notes/release_22_11.rst
@@ -63,6 +63,12 @@ New Features
   In theory this implementation should work with any target based on
   ``LoongArch`` ISA.
 
+* **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.
+
 * **Added support for multiple mbuf pools per ethdev Rx queue.**
 
   The capability allows application to provide many mempools
diff --git a/lib/eal/common/eal_common_log.c b/lib/eal/common/eal_common_log.c
index bd7b188ceb..3d62af59c6 100644
--- a/lib/eal/common/eal_common_log.c
+++ b/lib/eal/common/eal_common_log.c
@@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
 	{RTE_LOGTYPE_EFD,        "lib.efd"},
 	{RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
 	{RTE_LOGTYPE_GSO,        "lib.gso"},
+	{RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
 	{RTE_LOGTYPE_USER1,      "user1"},
 	{RTE_LOGTYPE_USER2,      "user2"},
 	{RTE_LOGTYPE_USER3,      "user3"},
diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
index 25ce42cdfc..708f3a39dd 100644
--- a/lib/eal/include/rte_log.h
+++ b/lib/eal/include/rte_log.h
@@ -48,6 +48,7 @@ extern "C" {
 #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
 #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
 #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
+#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..59be9c1d00
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef MEMAREA_PRIVATE_H
+#define MEMAREA_PRIVATE_H
+
+#include <rte_memarea.h>
+
+#define MEMAREA_AVAILABLE_ELEM_MAGIC	0xbeef1234
+#define MEMAREA_ALLOCATED_ELEM_MAGIC	0xbeef1230
+#define MEMAREA_AVAILABLE_ELEM_COOKIE	0xffffffff
+
+struct memarea_elem {
+	TAILQ_ENTRY(memarea_elem) elem_node;
+	TAILQ_ENTRY(memarea_elem) free_node;
+	size_t   size;
+	uint32_t magic;
+	uint32_t cookie;
+	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
+} __rte_cache_aligned;
+
+TAILQ_HEAD(memarea_elem_list, memarea_elem);
+
+struct rte_memarea {
+	struct rte_memarea_param init;
+	rte_spinlock_t           lock;
+	void                    *area_addr;
+	struct memarea_elem_list elem_list;
+	struct memarea_elem_list free_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..0a74fb4cd1
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2022 HiSilicon Limited
+
+if is_windows
+    build = false
+    reason = 'not supported on Windows'
+    subdir_done()
+endif
+
+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..60b8fa8612
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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"
+
+static int
+memarea_check_param(const struct rte_memarea_param *init)
+{
+	size_t len;
+
+	if (init == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
+		return -EINVAL;
+	}
+
+	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
+	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
+		return -EINVAL;
+	}
+
+	if (init->source != RTE_MEMAREA_SOURCE_HEAP &&
+	    init->source != RTE_MEMAREA_SOURCE_LIBC &&
+	    init->source != RTE_MEMAREA_SOURCE_MEMAREA) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
+			init->name, init->source);
+		return -EINVAL;
+	}
+
+	if (init->total_sz <= sizeof(struct memarea_elem)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
+			init->name, init->total_sz);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_MEMAREA && init->src_memarea == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source memarea is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->alg != RTE_MEMAREA_ALG_NEXTFIT) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
+			init->name, init->alg);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+memarea_alloc_from_libc(size_t size)
+{
+	void *ptr = NULL;
+	int ret;
+
+	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+	if (ret)
+		return NULL;
+	return ptr;
+}
+
+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);
+
+	if (ptr == NULL)
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
+
+	return ptr;
+}
+
+struct rte_memarea *
+rte_memarea_create(const struct rte_memarea_param *init)
+{
+	struct memarea_elem *elem;
+	struct rte_memarea *ma;
+	void *addr;
+	int ret;
+
+	ret = memarea_check_param(init);
+	if (ret)
+		return NULL;
+
+	addr = memarea_alloc_area(init);
+	if (addr == NULL)
+		return NULL;
+
+	ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
+	if (ma == NULL) {
+		RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
+		return NULL;
+	}
+
+	ma->init = *init;
+	rte_spinlock_init(&ma->lock);
+	TAILQ_INIT(&ma->elem_list);
+	TAILQ_INIT(&ma->free_list);
+	ma->area_addr = addr;
+	elem = addr;
+	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
+	TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+	elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+	elem->refcnt = 0;
+
+	return ma;
+}
+
+static void
+memarea_free_area(struct rte_memarea *ma)
+{
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		rte_free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
+		free(ma->area_addr);
+}
+
+void
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+	if (ma == NULL)
+		return;
+	memarea_free_area(ma);
+	rte_free(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
new file mode 100644
index 0000000000..d48ceaf733
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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 provides refcnt feature which could be useful in multi-reader scenario.
+ *
+ * - 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_alg {
+	/** The default management algorithm is a variant of the next fit
+	 * algorithm. It uses a free-list to apply for memory and uses an
+	 * element-list in ascending order of address to support merging
+	 * upon free.
+	 */
+	RTE_MEMAREA_ALG_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_alg 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_memarea;
+	};
+};
+
+/**
+ * @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 */
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 c51cdc24fa..87d621e792 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v9 2/7] test/memarea: support memarea test
  2022-10-12 10:47 ` [PATCH v9 0/7] introduce memarea library Chengwen Feng
  2022-10-12 10:47   ` [PATCH v9 1/7] memarea: " Chengwen Feng
@ 2022-10-12 10:48   ` Chengwen Feng
  2022-10-12 10:48   ` [PATCH v9 3/7] memarea: support alloc/free/refcnt-update API Chengwen Feng
                     ` (4 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-12 10:48 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports memarea test of rte_memarea_create() and
rte_memarea_destroy() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 142 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 145 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3d8d4c2dbe..38625ae1be 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1555,6 +1555,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index 396b133959..8943f24668 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -84,6 +84,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -200,6 +201,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..b25af1880a
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#ifdef RTE_EXEC_ENV_WINDOWS
+static int
+test_memarea(void)
+{
+	printf("memarea not supported on Windows, skipping test\n");
+	return TEST_SKIPPED;
+}
+
+#else
+
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) { \
+			printf("%s Failed\n", #test_func); \
+			fails++; \
+		} else { \
+			printf("%s Passed\n", #test_func); \
+		} \
+	} while (0)
+
+static int fails;
+
+static void
+test_memarea_prepare(void)
+{
+	fails = 0;
+}
+
+static int
+test_memarea_retcode(void)
+{
+	return fails > 0 ? -1 : 0;
+}
+
+static void
+test_memarea_init_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "test-memarea");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for memarea NULL */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for alg invalid */
+	test_memarea_init_param(&init);
+	init.alg = RTE_MEMAREA_ALG_NEXTFIT + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for create with HEAP */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.numa_socket = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	test_memarea_prepare();
+
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+
+	return test_memarea_retcode();
+}
+
+#endif /* !RTE_EXEC_ENV_WINDOWS */
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v9 3/7] memarea: support alloc/free/refcnt-update API
  2022-10-12 10:47 ` [PATCH v9 0/7] introduce memarea library Chengwen Feng
  2022-10-12 10:47   ` [PATCH v9 1/7] memarea: " Chengwen Feng
  2022-10-12 10:48   ` [PATCH v9 2/7] test/memarea: support memarea test Chengwen Feng
@ 2022-10-12 10:48   ` Chengwen Feng
  2022-10-12 10:48   ` [PATCH v9 4/7] test/memarea: support alloc/free/refcnt-update test Chengwen Feng
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-12 10:48 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_refcnt_update() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  10 ++
 lib/memarea/memarea_private.h         |   3 +
 lib/memarea/rte_memarea.c             | 157 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  74 ++++++++++++
 lib/memarea/version.map               |   3 +
 5 files changed, 247 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index d032b24ce9..8fbfd06a5d 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,16 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
+The ``rte_memarea_refcnt_update()`` function is used to update the memory
+object's reference count, if the count reaches zero, the memory object will
+be freed to memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 59be9c1d00..f5accf2987 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -28,6 +28,9 @@ struct rte_memarea {
 	void                    *area_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
+
+	uint64_t alloc_fails;
+	uint64_t refcnt_check_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 60b8fa8612..178430900e 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -4,6 +4,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -79,6 +80,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 					init->numa_socket);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		ptr = memarea_alloc_from_libc(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		ptr = rte_memarea_alloc(init->src_memarea, init->total_sz, 0);
 
 	if (ptr == NULL)
 		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
@@ -131,6 +134,8 @@ memarea_free_area(struct rte_memarea *ma)
 		rte_free(ma->area_addr);
 	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		rte_memarea_free(ma->init.src_memarea, ma->area_addr);
 }
 
 void
@@ -141,3 +146,155 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(ma);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+static inline bool
+memarea_whether_add_node(size_t free_size, size_t need_size)
+{
+	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
+	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
+}
+
+static inline void
+memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
+{
+	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
+	struct memarea_elem *new_elem;
+
+	new_elem = (struct memarea_elem *)RTE_PTR_ADD(elem, sizeof(struct memarea_elem) +
+							    align_size);
+	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+	new_elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+	new_elem->refcnt = 0;
+	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
+	TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
+	elem->size = align_size;
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	struct memarea_elem *elem;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0))
+		return NULL;
+
+	memarea_lock(ma);
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (unlikely(elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC))
+			break;
+		if (elem->size < size)
+			continue;
+		if (memarea_whether_add_node(elem->size, size))
+			memarea_add_node(ma, elem, size);
+		elem->magic = MEMAREA_ALLOCATED_ELEM_MAGIC;
+		elem->cookie = cookie;
+		elem->refcnt = 1;
+		TAILQ_REMOVE(&ma->free_list, elem, free_node);
+		ptr = RTE_PTR_ADD(elem, sizeof(struct memarea_elem));
+		break;
+	}
+	if (unlikely(ptr == NULL))
+		ma->alloc_fails++;
+	memarea_unlock(ma);
+
+	return ptr;
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	if (unlikely(ma == NULL || ptr == NULL))
+		return;
+
+	rte_memarea_refcnt_update(ma, ptr, -1);
+}
+
+static inline void
+memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
+		   struct memarea_elem *next, bool del_next_from_free,
+		   bool add_curr_to_free)
+{
+	curr->size += next->size + sizeof(struct memarea_elem);
+	next->size = 0;
+	next->magic = 0;
+	next->cookie = 0;
+	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
+	if (del_next_from_free)
+		TAILQ_REMOVE(&ma->free_list, next, free_node);
+	if (add_curr_to_free) {
+		curr->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+		curr->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
+	}
+}
+
+static inline void
+memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
+{
+	struct memarea_elem *prev, *next;
+	bool merged = false;
+
+	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
+	next = TAILQ_NEXT(elem, elem_node);
+	if (prev != NULL && prev->refcnt == 0) {
+		memarea_merge_node(ma, prev, elem, false, false);
+		elem = prev;
+		merged = true;
+	}
+	if (next != NULL && next->refcnt == 0) {
+		memarea_merge_node(ma, elem, next, true, !merged);
+		merged = true;
+	}
+	if (!merged) {
+		elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+		elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	}
+}
+
+void
+rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value)
+{
+	struct memarea_elem *elem = (struct memarea_elem *)RTE_PTR_SUB(ptr,
+							    sizeof(struct memarea_elem));
+
+	memarea_lock(ma);
+	if (unlikely(elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail!\n",
+			ma->init.name, elem->magic);
+		memarea_unlock(ma);
+		return;
+	}
+
+	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
+		RTE_LOG(ERR, MEMAREA,
+			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
+			ma->init.name, elem->cookie, elem->refcnt, value);
+		ma->refcnt_check_fails++;
+		if (elem->refcnt > 0)
+			elem->refcnt += value;
+		memarea_unlock(ma);
+		return;
+	}
+
+	elem->refcnt += value;
+	if (elem->refcnt == 0)
+		memarea_free_elem(ma, elem);
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index d48ceaf733..31c499ec07 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -117,6 +117,80 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ * @param cookie
+ *   User-provided footprint which could used to debug memory leak.
+ *
+ * @return
+ *   - NULL on error. Not enough memory, or invalid arguments (ma is NULL,
+ *     size is 0).
+ *   - Otherwise, the pointer to the allocated object.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea. If the object's refcnt is greater
+ * than one, only the refcnt is decremented by one. Otherwise the object is
+ * released.
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), if it is allocated from memarea-A, it must be freed to
+ * the same memarea-A. The behaviour of rte_memarea_free() is undefined if the
+ * memarea or pointer does not match these requirements.
+ *
+ * @param ma
+ *   The pointer of memarea. If the ma is NULL, the function does nothing.
+ * @param ptr
+ *   The pointer of memory object which need be freed. If the pointer is NULL,
+ *   the function does nothing.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Update memory's refcnt.
+ *
+ * Update one memory object's refcnt. When refcnt is updated to be zero, the
+ * memory object is freed.
+ *
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), if it is allocated from memarea-A, the refcnt update
+ * must be operated to the same memarea-A. The behaviour of
+ * rte_memarea_refcnt_update() is undefined if the memarea or pointer does not
+ * match these requirements.
+ *
+ * @note If the memory object's refcnt updated to be lower than zero, an error
+ * message will be printed, and the memory object will not freed to memrea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be updated refcnt.
+ * @param value
+ *   The value which need be updated.
+ */
+__rte_experimental
+void rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..a087d40d5f 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,11 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
+	rte_memarea_refcnt_update;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v9 4/7] test/memarea: support alloc/free/refcnt-update test
  2022-10-12 10:47 ` [PATCH v9 0/7] introduce memarea library Chengwen Feng
                     ` (2 preceding siblings ...)
  2022-10-12 10:48   ` [PATCH v9 3/7] memarea: support alloc/free/refcnt-update API Chengwen Feng
@ 2022-10-12 10:48   ` Chengwen Feng
  2022-10-12 10:48   ` [PATCH v9 5/7] memarea: support dump API Chengwen Feng
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-12 10:48 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_refcnt_update() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 145 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 144 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index b25af1880a..5252d47fc1 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -57,6 +57,12 @@ test_memarea_init_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -105,8 +111,8 @@ test_memarea_create_bad_param(void)
 static int
 test_memarea_create_destroy(void)
 {
+	struct rte_memarea *ma, *src_ma;
 	struct rte_memarea_param init;
-	struct rte_memarea *ma;
 
 	/* test for create with HEAP */
 	test_memarea_init_param(&init);
@@ -123,6 +129,140 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with another memarea */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	src_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(src_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.src_memarea = src_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(src_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE - RTE_CACHE_LINE_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	ptr[1] = rte_memarea_alloc(ma, 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test free with refcnt fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	/* test update refcnt with fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_refcnt_update(ma, ptr, -2);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[ALLOC_MAX_NUM];
+	int i;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1, 0);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, NULL);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -133,6 +273,9 @@ test_memarea(void)
 
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v9 5/7] memarea: support dump API
  2022-10-12 10:47 ` [PATCH v9 0/7] introduce memarea library Chengwen Feng
                     ` (3 preceding siblings ...)
  2022-10-12 10:48   ` [PATCH v9 4/7] test/memarea: support alloc/free/refcnt-update test Chengwen Feng
@ 2022-10-12 10:48   ` Chengwen Feng
  2022-10-12 10:48   ` [PATCH v9 6/7] test/memarea: support dump test Chengwen Feng
  2022-10-12 10:48   ` [PATCH v9 7/7] app/test: add memarea to malloc-perf-autotest Chengwen Feng
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-12 10:48 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +
 lib/memarea/rte_memarea.c             | 98 +++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 21 ++++++
 lib/memarea/version.map               |  1 +
 4 files changed, 123 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 8fbfd06a5d..c33a548417 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -43,6 +43,9 @@ The ``rte_memarea_refcnt_update()`` function is used to update the memory
 object's reference count, if the count reaches zero, the memory object will
 be freed to memarea.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 178430900e..629e7a5ffc 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2022 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/queue.h>
@@ -298,3 +299,100 @@ rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value)
 		memarea_free_elem(ma, elem);
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_MEMAREA)
+		return "memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_alg alg)
+{
+	if (alg == RTE_MEMAREA_ALG_NEXTFIT)
+		return "nextfit";
+	else
+		return "unknown";
+}
+
+static uint32_t
+memarea_elem_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC &&
+		    elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC)
+			break;
+		num++;
+	}
+
+	return num;
+}
+
+static uint32_t
+memarea_free_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC)
+			break;
+		num++;
+	}
+
+	return num;
+}
+
+static void
+memarea_dump_all(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_elem *elem;
+
+	fprintf(f, "  regions:\n");
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC &&
+		    elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC) {
+			fprintf(f, "    magic: 0x%x check fail!\n", elem->magic);
+			break;
+		}
+		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
+			elem->size, elem->cookie, elem->refcnt);
+	}
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		fprintf(f, "  heap-numa-socket: %d\n", ma->init.numa_socket);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		fprintf(f, "  source-memarea: %s\n", ma->init.src_memarea->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
+	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
+	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
+	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (dump_all)
+		memarea_dump_all(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 31c499ec07..61042a8d0d 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -191,6 +191,27 @@ void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 __rte_experimental
 void rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index a087d40d5f..6d75f9f7c4 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 	rte_memarea_refcnt_update;
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v9 6/7] test/memarea: support dump test
  2022-10-12 10:47 ` [PATCH v9 0/7] introduce memarea library Chengwen Feng
                     ` (4 preceding siblings ...)
  2022-10-12 10:48   ` [PATCH v9 5/7] memarea: support dump API Chengwen Feng
@ 2022-10-12 10:48   ` Chengwen Feng
  2022-10-12 10:48   ` [PATCH v9 7/7] app/test: add memarea to malloc-perf-autotest Chengwen Feng
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-12 10:48 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_dump() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 5252d47fc1..5aa0683427 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -266,6 +266,40 @@ test_memarea_alloc_free(void)
 	return 0;
 }
 
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -276,6 +310,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v9 7/7] app/test: add memarea to malloc-perf-autotest
  2022-10-12 10:47 ` [PATCH v9 0/7] introduce memarea library Chengwen Feng
                     ` (5 preceding siblings ...)
  2022-10-12 10:48   ` [PATCH v9 6/7] test/memarea: support dump test Chengwen Feng
@ 2022-10-12 10:48   ` Chengwen Feng
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-12 10:48 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch adds memarea to malloc_perf_autotest.

Test platform: Kunpeng920
Test command: dpdk-test -a 0000:7d:00.3 -l 10-12
Test result:
USER1: Performance: rte_memarea
USER1:    Size (B)   Runs  Alloc (us) Free (us) Total (us)  memset (us)
USER1:          64  10000        0.04      0.04       0.08         0.01
USER1:         128  10000        0.03      0.03       0.06         0.01
USER1:        1024  10000        0.03      0.04       0.07         0.20
USER1:        4096  10000        0.03      0.05       0.08         0.34
USER1:       65536  10000        0.10      0.06       0.16         2.15
USER1:     1048576   1023        0.11      0.04       0.15        29.15
USER1:     2097152    511        0.12      0.04       0.16        57.72
USER1:     4194304    255        0.14      0.04       0.17       114.93
USER1:    16777216     63        0.15      0.07       0.21       457.51
USER1:  1073741824 Interrupted: out of memory.

Compared with rte_malloc:
USER1: Performance: rte_malloc
USER1:    Size (B)   Runs  Alloc (us) Free (us) Total (us)  memset (us)
USER1:          64  10000        0.14      0.07       0.21         0.01
USER1:         128  10000        0.10      0.05       0.15         0.01
USER1:        1024  10000        0.11      0.18       0.29         0.21
USER1:        4096  10000        0.13      0.39       0.53         0.35
USER1:       65536  10000        0.17      2.27       2.44         2.15
USER1:     1048576  10000       37.21     71.63     108.84        29.08
USER1:     2097152  10000     8831.15    160.02    8991.17        63.52
USER1:     4194304  10000    47131.88    413.75   47545.62       173.79
USER1:    16777216   4221   119604.60   2209.73  121814.34       964.42
USER1:  1073741824     31   335058.32 223369.31  558427.63     62440.87

Note: The rte_malloc time includes obtaining memory from the system,
but rte_memarea time don't includes these (it uses pre-allocated
policy).

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_malloc_perf.c | 75 +++++++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/app/test/test_malloc_perf.c b/app/test/test_malloc_perf.c
index ccec43ae84..2437c7ff4b 100644
--- a/app/test/test_malloc_perf.c
+++ b/app/test/test_malloc_perf.c
@@ -147,6 +147,78 @@ memzone_free(void *addr)
 	rte_memzone_free((struct rte_memzone *)addr);
 }
 
+#ifndef RTE_EXEC_ENV_WINDOWS
+#include <rte_memarea.h>
+
+static struct rte_memarea *test_ma;
+
+static int
+memarea_pre_env(void)
+{
+	struct rte_memarea_param init;
+
+	memset(&init, 0, sizeof(init));
+	snprintf(init.name, sizeof(init.name), "perftest");
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.alg = RTE_MEMAREA_ALG_NEXTFIT;
+	init.total_sz = GB;
+	init.mt_safe = 1;
+	init.numa_socket = SOCKET_ID_ANY;
+
+	test_ma = rte_memarea_create(&init);
+	if (test_ma == NULL) {
+		fprintf(stderr, "memarea create failed, skip memarea perftest!\n");
+		return -1;
+	}
+	return 0;
+}
+
+static void
+memarea_clear_env(void)
+{
+	rte_memarea_destroy(test_ma);
+	test_ma = NULL;
+}
+
+static void *
+memarea_alloc(const char *name, size_t size, unsigned int align)
+{
+	RTE_SET_USED(name);
+	RTE_SET_USED(align);
+	return rte_memarea_alloc(test_ma, size, 0);
+}
+
+static void
+memarea_free(void *addr)
+{
+	rte_memarea_free(test_ma, addr);
+}
+
+static int
+memarea_perftest(double memset_gb_us, size_t max_runs)
+{
+	if (memarea_pre_env() < 0)
+		return 0;
+
+	if (test_alloc_perf("rte_memarea", memarea_alloc, memarea_free,
+			memset, memset_gb_us, max_runs) < 0) {
+		memarea_clear_env();
+		return -1;
+	}
+
+	memarea_clear_env();
+	return 0;
+}
+#else
+static int
+memarea_perftest(double memset_gb_us, size_t max_runs)
+{
+	RTE_SET_USED(memset_gb_us);
+	RTE_SET_USED(max_runs);
+	return 0;
+}
+#endif /* !RTE_EXEC_ENV_WINDOWS */
+
 static int
 test_malloc_perf(void)
 {
@@ -168,6 +240,9 @@ test_malloc_perf(void)
 			NULL, memset_us_gb, RTE_MAX_MEMZONE - 1) < 0)
 		return -1;
 
+	if (memarea_perftest(memset_us_gb, MAX_RUNS) < 0)
+		return -1;
+
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v8 1/9] memarea: introduce memarea library
  2022-10-12  4:06       ` fengchengwen
@ 2022-10-13 10:45         ` fengchengwen
  0 siblings, 0 replies; 222+ messages in thread
From: fengchengwen @ 2022-10-13 10:45 UTC (permalink / raw)
  To: Dmitry Kozlyuk
  Cc: david.marchand, mb, anatoly.burakov, jerinjacobk, hofors, thomas, dev

Sorry to self-reply.

On 2022/10/12 12:06, fengchengwen wrote:
> Hi Dmitry,
> 
> On 2022/10/11 23:58, Dmitry Kozlyuk wrote:
>> 2022-10-11 12:17 (UTC+0000), Chengwen Feng:
>> [...]
>>> diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
>>> new file mode 100644
>>> index 0000000000..85ad57145f
>>> --- /dev/null
>>> +++ b/doc/guides/prog_guide/memarea_lib.rst
>>> @@ -0,0 +1,39 @@
>>> +..  SPDX-License-Identifier: BSD-3-Clause
>>> +    Copyright(c) 2022 HiSilicon Limited
>>> +
>>> +Memarea Library
>>> +===============
>>> +
>>> +Introduction
>>> +------------
>>> +
>>> +The memarea library provides an allocator of variable-size objects, it is
>>> +oriented towards the application layer, which could provides 'region-based
>>> +memory management' function [1].
>>> +
>>> +The main features are as follows:
>>> +
>>> +* The allocated object aligned at ``RTE_CACHE_LINE_SIZE`` default.
>>
>> Isn't this an implementation detail?
>> Stating it in the API description limits optimization opportunities.
>> Cache line alignment is good in many cases,
>> but it can also be a waste of space,
>> e.g. for a thread-unsafe region for small objects.
>> Can this limitation only (temporarily?) apply to user memory?
>> Or can the minimal alignment be a property of memarea?
> 
> You are right, to be more general, I'll add the align parameter to rte_memarea_alloc instead of stating it here.

The memarea is targeted to not so general usage, so v9 doesn't add align parameter to rte_memarea_alloc, at least from the internal feedback.
We could add rte_memarea_align_alloc() if the align requirement does exist.

> 
>>
>>> +
>>> +* The memory region can be initialized from the following memory sources:
>>> +  a) HEAP: e.g. invoke ``rte_malloc_socket``. b) LIBC: e.g. invoke
>>> +  posix_memalign to obtain. c) User memory: it can be from e.g. rte_extmem_xxx
>>> +  as long as it is available. d) Another memarea: it can be allocated from
>>> +  another memarea.
>>
>> I think mentioning rte_extmem_xxx() is bogus
>> because user memory does not need to be registered with DPDK
>> (I understand it's an example, but still an unrelated reference).
>> Please format as a list.
> 
> okay
> 
>>
>>> +
>>> +* It provides refcnt feature which could be useful in multi-reader scenario.
>>> +
>>> +* 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.
>>> +
>>> +Reference
>>> +---------
>>> +
>>> +[1] https://en.wikipedia.org/wiki/Region-based_memory_management
>>> diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
>>> index 2da8bc9661..f5a67cec7b 100644
>>> --- a/doc/guides/rel_notes/release_22_11.rst
>>> +++ b/doc/guides/rel_notes/release_22_11.rst
>>> @@ -63,6 +63,12 @@ New Features
>>>    In theory this implementation should work with any target based on
>>>    ``LoongArch`` ISA.
>>>  
>>> +* **Added memarea library.**
>>> +
>>> +  The memarea library is an allocator of variable-size objects, it is oriented
>>> +  towards the application layer, which could provides 'region-based memory
>>> +  management' function.
>>
>> "which could provides" -> "providing"
> 
> okay
> 
>>
>>> +
>>>  * **Added support for multiple mbuf pools per ethdev Rx queue.**
>>>  
>>>    The capability allows application to provide many mempools
>> [...]
>> .
>>
> 
> .
> 

^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v10 0/7] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (13 preceding siblings ...)
  2022-10-12 10:47 ` [PATCH v9 0/7] introduce memarea library Chengwen Feng
@ 2022-10-17  3:40 ` Chengwen Feng
  2022-10-17  3:40   ` [PATCH v10 1/7] memarea: " Chengwen Feng
                     ` (6 more replies)
  2022-12-13  9:13 ` [PATCH v11 0/6] introduce memarea library Chengwen Feng
                   ` (8 subsequent siblings)
  23 siblings, 7 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-17  3:40 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- 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 from another memarea.

- It provides refcnt feature which could be useful in multi-reader
  scenario.

- It supports MT-safe as long as it's specified at creation time.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>

Chengwen Feng (7):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc/free/refcnt-update API
  test/memarea: support alloc/free/refcnt-update test
  memarea: support dump API
  test/memarea: support dump test
  app/test: add memarea to malloc-perf-autotest

---
v10:
* support windows platform.
* add rte_memarea.libc perftest to malloc-perf-autotest.
v9:
* address Dmitry's comments.
* drop features of SOURCE_USER and backup memarea mechanism.
* rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
  to keep with rte_mbuf_refcnt_update name style.
* fix memarea perftest compile failed at windows platform.
* fix spell warning.
v8:
* address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
* small feature patches are combined.
* enhanced backup memory mechanism.
* add memarea to malloc-perf-autotest.
* other tiny naming optimize.
v7:
* repost patches as there are spread over different series in patchwork.
v6:
* address Mattias's comments.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_malloc_perf.c            |  82 +++++
 app/test/test_memarea.c                | 308 ++++++++++++++++++
 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  |  52 ++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  36 +++
 lib/memarea/meson.build                |  10 +
 lib/memarea/rte_memarea.c              | 413 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 219 +++++++++++++
 lib/memarea/version.map                |  16 +
 lib/meson.build                        |   1 +
 17 files changed, 1157 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v10 1/7] memarea: introduce memarea library
  2022-10-17  3:40 ` [PATCH v10 0/7] introduce memarea library Chengwen Feng
@ 2022-10-17  3:40   ` Chengwen Feng
  2022-10-17  3:40   ` [PATCH v10 2/7] test/memarea: support memarea test Chengwen Feng
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-17  3:40 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

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>
---
 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  |  39 ++++++
 doc/guides/rel_notes/release_22_11.rst |   6 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  33 ++++++
 lib/memarea/meson.build                |  10 ++
 lib/memarea/rte_memarea.c              | 158 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 124 +++++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 14 files changed, 394 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 2bd4a55f1b..3d8d4c2dbe 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1551,6 +1551,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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index de488c7abf..24456604f8 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -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),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index f0886c3bd1..8334ebcbd6 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -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 \
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..d032b24ce9
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -0,0 +1,39 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2022 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 provides refcnt feature which could be useful in multi-reader scenario.
+
+* 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.
+
+Reference
+---------
+
+[1] https://en.wikipedia.org/wiki/Region-based_memory_management
diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst
index 2da8bc9661..f8e3a23db7 100644
--- a/doc/guides/rel_notes/release_22_11.rst
+++ b/doc/guides/rel_notes/release_22_11.rst
@@ -63,6 +63,12 @@ New Features
   In theory this implementation should work with any target based on
   ``LoongArch`` ISA.
 
+* **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.
+
 * **Added support for multiple mbuf pools per ethdev Rx queue.**
 
   The capability allows application to provide many mempools
diff --git a/lib/eal/common/eal_common_log.c b/lib/eal/common/eal_common_log.c
index bd7b188ceb..3d62af59c6 100644
--- a/lib/eal/common/eal_common_log.c
+++ b/lib/eal/common/eal_common_log.c
@@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
 	{RTE_LOGTYPE_EFD,        "lib.efd"},
 	{RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
 	{RTE_LOGTYPE_GSO,        "lib.gso"},
+	{RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
 	{RTE_LOGTYPE_USER1,      "user1"},
 	{RTE_LOGTYPE_USER2,      "user2"},
 	{RTE_LOGTYPE_USER3,      "user3"},
diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
index 25ce42cdfc..708f3a39dd 100644
--- a/lib/eal/include/rte_log.h
+++ b/lib/eal/include/rte_log.h
@@ -48,6 +48,7 @@ extern "C" {
 #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
 #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
 #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
+#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..59be9c1d00
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef MEMAREA_PRIVATE_H
+#define MEMAREA_PRIVATE_H
+
+#include <rte_memarea.h>
+
+#define MEMAREA_AVAILABLE_ELEM_MAGIC	0xbeef1234
+#define MEMAREA_ALLOCATED_ELEM_MAGIC	0xbeef1230
+#define MEMAREA_AVAILABLE_ELEM_COOKIE	0xffffffff
+
+struct memarea_elem {
+	TAILQ_ENTRY(memarea_elem) elem_node;
+	TAILQ_ENTRY(memarea_elem) free_node;
+	size_t   size;
+	uint32_t magic;
+	uint32_t cookie;
+	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
+} __rte_cache_aligned;
+
+TAILQ_HEAD(memarea_elem_list, memarea_elem);
+
+struct rte_memarea {
+	struct rte_memarea_param init;
+	rte_spinlock_t           lock;
+	void                    *area_addr;
+	struct memarea_elem_list elem_list;
+	struct memarea_elem_list free_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..d5c2c442f5
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2022 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..41e5c007c2
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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"
+
+static int
+memarea_check_param(const struct rte_memarea_param *init)
+{
+#define MEMAREA_MIN_SIZE	1024
+	size_t len;
+
+	if (init == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
+		return -EINVAL;
+	}
+
+	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
+	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
+		return -EINVAL;
+	}
+
+	if (init->source != RTE_MEMAREA_SOURCE_HEAP &&
+	    init->source != RTE_MEMAREA_SOURCE_LIBC &&
+	    init->source != RTE_MEMAREA_SOURCE_MEMAREA) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
+			init->name, init->source);
+		return -EINVAL;
+	}
+
+	if (init->total_sz < MEMAREA_MIN_SIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
+			init->name, init->total_sz);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_MEMAREA && init->src_memarea == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source memarea is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->alg != RTE_MEMAREA_ALG_NEXTFIT) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
+			init->name, init->alg);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+memarea_alloc_from_libc(size_t size)
+{
+#ifndef RTE_EXEC_ENV_WINDOWS
+	void *ptr = NULL;
+	int ret;
+	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+	if (ret)
+		return NULL;
+	return ptr;
+#else
+	/* Windows platform don't support posix_memalign() */
+	return malloc(size);
+#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);
+
+	if (ptr == NULL)
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
+
+	return ptr;
+}
+
+struct rte_memarea *
+rte_memarea_create(const struct rte_memarea_param *init)
+{
+	struct memarea_elem *elem;
+	struct rte_memarea *ma;
+	size_t unaligns;
+	void *addr;
+	int ret;
+
+	ret = memarea_check_param(init);
+	if (ret)
+		return NULL;
+
+	addr = memarea_alloc_area(init);
+	if (addr == NULL)
+		return NULL;
+
+	ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
+	if (ma == NULL) {
+		RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
+		return NULL;
+	}
+
+	ma->init = *init;
+	rte_spinlock_init(&ma->lock);
+	TAILQ_INIT(&ma->elem_list);
+	TAILQ_INIT(&ma->free_list);
+	ma->area_addr = addr;
+	/* The address returned by the windows platform may not be cache-line aligned.
+	 * The elem address and total_sz are adjusted.
+	 */
+	unaligns = ((uintptr_t)addr) & (RTE_CACHE_LINE_SIZE - 1);
+	if (unaligns > 0) {
+		elem = RTE_PTR_ADD(addr, RTE_CACHE_LINE_SIZE - unaligns);
+		ma->init.total_sz -= (RTE_CACHE_LINE_SIZE - unaligns);
+	} else {
+		elem = addr;
+	}
+	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
+	TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+	elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+	elem->refcnt = 0;
+
+	return ma;
+}
+
+static void
+memarea_free_area(struct rte_memarea *ma)
+{
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		rte_free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
+		free(ma->area_addr);
+}
+
+void
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+	if (ma == NULL)
+		return;
+	memarea_free_area(ma);
+	rte_free(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
new file mode 100644
index 0000000000..d48ceaf733
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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 provides refcnt feature which could be useful in multi-reader scenario.
+ *
+ * - 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_alg {
+	/** The default management algorithm is a variant of the next fit
+	 * algorithm. It uses a free-list to apply for memory and uses an
+	 * element-list in ascending order of address to support merging
+	 * upon free.
+	 */
+	RTE_MEMAREA_ALG_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_alg 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_memarea;
+	};
+};
+
+/**
+ * @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 */
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 c51cdc24fa..87d621e792 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v10 2/7] test/memarea: support memarea test
  2022-10-17  3:40 ` [PATCH v10 0/7] introduce memarea library Chengwen Feng
  2022-10-17  3:40   ` [PATCH v10 1/7] memarea: " Chengwen Feng
@ 2022-10-17  3:40   ` Chengwen Feng
  2022-10-17  3:40   ` [PATCH v10 3/7] memarea: support alloc/free/refcnt-update API Chengwen Feng
                     ` (4 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-17  3:40 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports memarea test of rte_memarea_create() and
rte_memarea_destroy() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 130 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3d8d4c2dbe..38625ae1be 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1555,6 +1555,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index 396b133959..8943f24668 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -84,6 +84,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -200,6 +201,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..cfd50de45f
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) { \
+			printf("%s Failed\n", #test_func); \
+			fails++; \
+		} else { \
+			printf("%s Passed\n", #test_func); \
+		} \
+	} while (0)
+
+static int fails;
+
+static void
+test_memarea_prepare(void)
+{
+	fails = 0;
+}
+
+static int
+test_memarea_retcode(void)
+{
+	return fails > 0 ? -1 : 0;
+}
+
+static void
+test_memarea_init_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "test-memarea");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for memarea NULL */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for alg invalid */
+	test_memarea_init_param(&init);
+	init.alg = RTE_MEMAREA_ALG_NEXTFIT + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for create with HEAP */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.numa_socket = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	test_memarea_prepare();
+
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+
+	return test_memarea_retcode();
+}
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v10 3/7] memarea: support alloc/free/refcnt-update API
  2022-10-17  3:40 ` [PATCH v10 0/7] introduce memarea library Chengwen Feng
  2022-10-17  3:40   ` [PATCH v10 1/7] memarea: " Chengwen Feng
  2022-10-17  3:40   ` [PATCH v10 2/7] test/memarea: support memarea test Chengwen Feng
@ 2022-10-17  3:40   ` Chengwen Feng
  2022-10-17  3:40   ` [PATCH v10 4/7] test/memarea: support alloc/free/refcnt-update test Chengwen Feng
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-17  3:40 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_refcnt_update() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  10 ++
 lib/memarea/memarea_private.h         |   3 +
 lib/memarea/rte_memarea.c             | 157 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  74 ++++++++++++
 lib/memarea/version.map               |   3 +
 5 files changed, 247 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index d032b24ce9..8fbfd06a5d 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,16 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
+The ``rte_memarea_refcnt_update()`` function is used to update the memory
+object's reference count, if the count reaches zero, the memory object will
+be freed to memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 59be9c1d00..f5accf2987 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -28,6 +28,9 @@ struct rte_memarea {
 	void                    *area_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
+
+	uint64_t alloc_fails;
+	uint64_t refcnt_check_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 41e5c007c2..a0935dafb6 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -4,6 +4,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -84,6 +85,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 					init->numa_socket);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		ptr = memarea_alloc_from_libc(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		ptr = rte_memarea_alloc(init->src_memarea, init->total_sz, 0);
 
 	if (ptr == NULL)
 		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
@@ -146,6 +149,8 @@ memarea_free_area(struct rte_memarea *ma)
 		rte_free(ma->area_addr);
 	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		rte_memarea_free(ma->init.src_memarea, ma->area_addr);
 }
 
 void
@@ -156,3 +161,155 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(ma);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+static inline bool
+memarea_whether_add_node(size_t free_size, size_t need_size)
+{
+	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
+	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
+}
+
+static inline void
+memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
+{
+	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
+	struct memarea_elem *new_elem;
+
+	new_elem = (struct memarea_elem *)RTE_PTR_ADD(elem, sizeof(struct memarea_elem) +
+							    align_size);
+	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+	new_elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+	new_elem->refcnt = 0;
+	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
+	TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
+	elem->size = align_size;
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	struct memarea_elem *elem;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0))
+		return NULL;
+
+	memarea_lock(ma);
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (unlikely(elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC))
+			break;
+		if (elem->size < size)
+			continue;
+		if (memarea_whether_add_node(elem->size, size))
+			memarea_add_node(ma, elem, size);
+		elem->magic = MEMAREA_ALLOCATED_ELEM_MAGIC;
+		elem->cookie = cookie;
+		elem->refcnt = 1;
+		TAILQ_REMOVE(&ma->free_list, elem, free_node);
+		ptr = RTE_PTR_ADD(elem, sizeof(struct memarea_elem));
+		break;
+	}
+	if (unlikely(ptr == NULL))
+		ma->alloc_fails++;
+	memarea_unlock(ma);
+
+	return ptr;
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	if (unlikely(ma == NULL || ptr == NULL))
+		return;
+
+	rte_memarea_refcnt_update(ma, ptr, -1);
+}
+
+static inline void
+memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
+		   struct memarea_elem *next, bool del_next_from_free,
+		   bool add_curr_to_free)
+{
+	curr->size += next->size + sizeof(struct memarea_elem);
+	next->size = 0;
+	next->magic = 0;
+	next->cookie = 0;
+	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
+	if (del_next_from_free)
+		TAILQ_REMOVE(&ma->free_list, next, free_node);
+	if (add_curr_to_free) {
+		curr->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+		curr->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
+	}
+}
+
+static inline void
+memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
+{
+	struct memarea_elem *prev, *next;
+	bool merged = false;
+
+	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
+	next = TAILQ_NEXT(elem, elem_node);
+	if (prev != NULL && prev->refcnt == 0) {
+		memarea_merge_node(ma, prev, elem, false, false);
+		elem = prev;
+		merged = true;
+	}
+	if (next != NULL && next->refcnt == 0) {
+		memarea_merge_node(ma, elem, next, true, !merged);
+		merged = true;
+	}
+	if (!merged) {
+		elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+		elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	}
+}
+
+void
+rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value)
+{
+	struct memarea_elem *elem = (struct memarea_elem *)RTE_PTR_SUB(ptr,
+							    sizeof(struct memarea_elem));
+
+	memarea_lock(ma);
+	if (unlikely(elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail!\n",
+			ma->init.name, elem->magic);
+		memarea_unlock(ma);
+		return;
+	}
+
+	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
+		RTE_LOG(ERR, MEMAREA,
+			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
+			ma->init.name, elem->cookie, elem->refcnt, value);
+		ma->refcnt_check_fails++;
+		if (elem->refcnt > 0)
+			elem->refcnt += value;
+		memarea_unlock(ma);
+		return;
+	}
+
+	elem->refcnt += value;
+	if (elem->refcnt == 0)
+		memarea_free_elem(ma, elem);
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index d48ceaf733..31c499ec07 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -117,6 +117,80 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ * @param cookie
+ *   User-provided footprint which could used to debug memory leak.
+ *
+ * @return
+ *   - NULL on error. Not enough memory, or invalid arguments (ma is NULL,
+ *     size is 0).
+ *   - Otherwise, the pointer to the allocated object.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea. If the object's refcnt is greater
+ * than one, only the refcnt is decremented by one. Otherwise the object is
+ * released.
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), if it is allocated from memarea-A, it must be freed to
+ * the same memarea-A. The behaviour of rte_memarea_free() is undefined if the
+ * memarea or pointer does not match these requirements.
+ *
+ * @param ma
+ *   The pointer of memarea. If the ma is NULL, the function does nothing.
+ * @param ptr
+ *   The pointer of memory object which need be freed. If the pointer is NULL,
+ *   the function does nothing.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Update memory's refcnt.
+ *
+ * Update one memory object's refcnt. When refcnt is updated to be zero, the
+ * memory object is freed.
+ *
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), if it is allocated from memarea-A, the refcnt update
+ * must be operated to the same memarea-A. The behaviour of
+ * rte_memarea_refcnt_update() is undefined if the memarea or pointer does not
+ * match these requirements.
+ *
+ * @note If the memory object's refcnt updated to be lower than zero, an error
+ * message will be printed, and the memory object will not freed to memrea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be updated refcnt.
+ * @param value
+ *   The value which need be updated.
+ */
+__rte_experimental
+void rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..a087d40d5f 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,11 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
+	rte_memarea_refcnt_update;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v10 4/7] test/memarea: support alloc/free/refcnt-update test
  2022-10-17  3:40 ` [PATCH v10 0/7] introduce memarea library Chengwen Feng
                     ` (2 preceding siblings ...)
  2022-10-17  3:40   ` [PATCH v10 3/7] memarea: support alloc/free/refcnt-update API Chengwen Feng
@ 2022-10-17  3:40   ` Chengwen Feng
  2022-10-17  3:40   ` [PATCH v10 5/7] memarea: support dump API Chengwen Feng
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-17  3:40 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_refcnt_update() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 145 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 144 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index cfd50de45f..00f9d07ed4 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -47,6 +47,12 @@ test_memarea_init_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -95,8 +101,8 @@ test_memarea_create_bad_param(void)
 static int
 test_memarea_create_destroy(void)
 {
+	struct rte_memarea *ma, *src_ma;
 	struct rte_memarea_param init;
-	struct rte_memarea *ma;
 
 	/* test for create with HEAP */
 	test_memarea_init_param(&init);
@@ -113,6 +119,140 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with another memarea */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	src_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(src_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.src_memarea = src_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(src_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE - RTE_CACHE_LINE_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	ptr[1] = rte_memarea_alloc(ma, 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test free with refcnt fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	/* test update refcnt with fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_refcnt_update(ma, ptr, -2);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[ALLOC_MAX_NUM];
+	int i;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1, 0);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, NULL);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -123,6 +263,9 @@ test_memarea(void)
 
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v10 5/7] memarea: support dump API
  2022-10-17  3:40 ` [PATCH v10 0/7] introduce memarea library Chengwen Feng
                     ` (3 preceding siblings ...)
  2022-10-17  3:40   ` [PATCH v10 4/7] test/memarea: support alloc/free/refcnt-update test Chengwen Feng
@ 2022-10-17  3:40   ` Chengwen Feng
  2022-10-17  3:40   ` [PATCH v10 6/7] test/memarea: support dump test Chengwen Feng
  2022-10-17  3:40   ` [PATCH v10 7/7] app/test: add memarea to malloc-perf-autotest Chengwen Feng
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-17  3:40 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +
 lib/memarea/rte_memarea.c             | 98 +++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 21 ++++++
 lib/memarea/version.map               |  1 +
 4 files changed, 123 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 8fbfd06a5d..c33a548417 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -43,6 +43,9 @@ The ``rte_memarea_refcnt_update()`` function is used to update the memory
 object's reference count, if the count reaches zero, the memory object will
 be freed to memarea.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index a0935dafb6..c58f68df53 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2022 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/queue.h>
@@ -313,3 +314,100 @@ rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value)
 		memarea_free_elem(ma, elem);
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_MEMAREA)
+		return "memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_alg alg)
+{
+	if (alg == RTE_MEMAREA_ALG_NEXTFIT)
+		return "nextfit";
+	else
+		return "unknown";
+}
+
+static uint32_t
+memarea_elem_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC &&
+		    elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC)
+			break;
+		num++;
+	}
+
+	return num;
+}
+
+static uint32_t
+memarea_free_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC)
+			break;
+		num++;
+	}
+
+	return num;
+}
+
+static void
+memarea_dump_all(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_elem *elem;
+
+	fprintf(f, "  regions:\n");
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC &&
+		    elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC) {
+			fprintf(f, "    magic: 0x%x check fail!\n", elem->magic);
+			break;
+		}
+		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
+			elem->size, elem->cookie, elem->refcnt);
+	}
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		fprintf(f, "  heap-numa-socket: %d\n", ma->init.numa_socket);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		fprintf(f, "  source-memarea: %s\n", ma->init.src_memarea->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
+	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
+	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
+	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (dump_all)
+		memarea_dump_all(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 31c499ec07..61042a8d0d 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -191,6 +191,27 @@ void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 __rte_experimental
 void rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index a087d40d5f..6d75f9f7c4 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 	rte_memarea_refcnt_update;
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v10 6/7] test/memarea: support dump test
  2022-10-17  3:40 ` [PATCH v10 0/7] introduce memarea library Chengwen Feng
                     ` (4 preceding siblings ...)
  2022-10-17  3:40   ` [PATCH v10 5/7] memarea: support dump API Chengwen Feng
@ 2022-10-17  3:40   ` Chengwen Feng
  2022-10-17  3:40   ` [PATCH v10 7/7] app/test: add memarea to malloc-perf-autotest Chengwen Feng
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-17  3:40 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_dump() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 00f9d07ed4..da712a14e8 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -256,6 +256,40 @@ test_memarea_alloc_free(void)
 	return 0;
 }
 
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -266,6 +300,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v10 7/7] app/test: add memarea to malloc-perf-autotest
  2022-10-17  3:40 ` [PATCH v10 0/7] introduce memarea library Chengwen Feng
                     ` (5 preceding siblings ...)
  2022-10-17  3:40   ` [PATCH v10 6/7] test/memarea: support dump test Chengwen Feng
@ 2022-10-17  3:40   ` Chengwen Feng
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-10-17  3:40 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch adds memarea perftest to malloc_perf_autotest.

The memarea perftest contains two part:
1) rte_memarea.heap: the memory source comes from invoking
rte_malloc_socket().
2) rte_memarea.libc: the memory source comes from invoking malloc().

Test platform: Kunpeng920
Test command: dpdk-test -a 0000:7d:00.3 -l 10-12
Test result:
USER1: Performance: rte_memarea.heap
USER1:    Size (B)   Runs  Alloc (us) Free (us) Total (us)  memset (us)
USER1:          64  10000        0.04      0.04       0.08         0.01
USER1:         128  10000        0.03      0.03       0.06         0.01
USER1:        1024  10000        0.03      0.04       0.07         0.20
USER1:        4096  10000        0.03      0.05       0.08         0.34
USER1:       65536  10000        0.10      0.06       0.16         2.15
USER1:     1048576   1023        0.11      0.04       0.15        29.15
USER1:     2097152    511        0.12      0.04       0.16        57.72
USER1:     4194304    255        0.14      0.04       0.17       114.93
USER1:    16777216     63        0.15      0.07       0.21       457.51
USER1:  1073741824 Interrupted: out of memory.

USER1: Performance: rte_memarea.libc
USER1:    Size (B)   Runs  Alloc (us) Free (us) Total (us)  memset (us)
USER1:          64  10000        0.12      0.03       0.15         0.01
USER1:         128  10000        0.03      0.03       0.06         0.01
USER1:        1024  10000        0.15      0.04       0.19         0.19
USER1:        4096  10000        0.51      0.04       0.54         0.34
USER1:       65536  10000        9.40      0.06       9.46         2.14
USER1:     1048576   1023       48.39      0.07      48.46        29.15
USER1:     2097152    511        0.11      0.06       0.17        57.79
USER1:     4194304    255        0.11      0.06       0.18       114.82
USER1:    16777216     63        0.12      0.07       0.19       457.86
USER1:  1073741824 Interrupted: out of memory.

Compared with rte_malloc:
USER1: Performance: rte_malloc
USER1:    Size (B)   Runs  Alloc (us) Free (us) Total (us)  memset (us)
USER1:          64  10000        0.14      0.07       0.21         0.01
USER1:         128  10000        0.10      0.05       0.15         0.01
USER1:        1024  10000        0.11      0.18       0.29         0.21
USER1:        4096  10000        0.13      0.39       0.53         0.35
USER1:       65536  10000        0.17      2.27       2.44         2.15
USER1:     1048576  10000       37.21     71.63     108.84        29.08
USER1:     2097152  10000     8831.15    160.02    8991.17        63.52
USER1:     4194304  10000    47131.88    413.75   47545.62       173.79
USER1:    16777216   4221   119604.60   2209.73  121814.34       964.42
USER1:  1073741824     31   335058.32 223369.31  558427.63     62440.87

Note: The rte_malloc time includes obtaining memory from the system,
but rte_memarea time don't includes these (it uses pre-allocated
policy).

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_malloc_perf.c | 82 +++++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)

diff --git a/app/test/test_malloc_perf.c b/app/test/test_malloc_perf.c
index ccec43ae84..0972f4dec7 100644
--- a/app/test/test_malloc_perf.c
+++ b/app/test/test_malloc_perf.c
@@ -7,6 +7,7 @@
 #include <rte_cycles.h>
 #include <rte_errno.h>
 #include <rte_malloc.h>
+#include <rte_memarea.h>
 #include <rte_memzone.h>
 
 #include "test.h"
@@ -20,6 +21,8 @@ typedef void * (memset_t)(void *addr, int value, size_t size);
 static const uint64_t KB = 1 << 10;
 static const uint64_t GB = 1 << 30;
 
+static struct rte_memarea *ma_perftest_handle;
+
 static double
 tsc_to_us(uint64_t tsc, size_t runs)
 {
@@ -147,6 +150,80 @@ memzone_free(void *addr)
 	rte_memzone_free((struct rte_memzone *)addr);
 }
 
+static const char *
+memarea_perftest_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else
+		return "unknown";
+}
+
+static int
+memarea_perftest_pre_env(enum rte_memarea_source source)
+{
+	struct rte_memarea_param init;
+
+	memset(&init, 0, sizeof(init));
+	snprintf(init.name, sizeof(init.name), "perftest");
+	init.source = source;
+	init.alg = RTE_MEMAREA_ALG_NEXTFIT;
+	init.total_sz = GB;
+	init.mt_safe = 1;
+	init.numa_socket = SOCKET_ID_ANY;
+
+	ma_perftest_handle = rte_memarea_create(&init);
+	if (ma_perftest_handle == NULL) {
+		fprintf(stderr, "memarea create failed, skip memarea source: %s perftest!\n",
+			memarea_perftest_source_name(source));
+		return -1;
+	}
+	return 0;
+}
+
+static void
+memarea_perftest_clear_env(void)
+{
+	rte_memarea_destroy(ma_perftest_handle);
+	ma_perftest_handle = NULL;
+}
+
+static void *
+memarea_perftest_alloc(const char *name, size_t size, unsigned int align)
+{
+	RTE_SET_USED(name);
+	RTE_SET_USED(align);
+	return rte_memarea_alloc(ma_perftest_handle, size, 0);
+}
+
+static void
+memarea_perftest_free(void *addr)
+{
+	rte_memarea_free(ma_perftest_handle, addr);
+}
+
+static int
+memarea_perftest(enum rte_memarea_source source, double memset_gb_us, size_t max_runs)
+{
+	char test_name[64] = { 0 };
+
+	if (memarea_perftest_pre_env(source) < 0)
+		return 0;
+
+	snprintf(test_name, sizeof(test_name), "rte_memarea.%s",
+		 memarea_perftest_source_name(source));
+	if (test_alloc_perf(test_name, memarea_perftest_alloc, memarea_perftest_free,
+			memset, memset_gb_us, max_runs) < 0) {
+		memarea_perftest_clear_env();
+		return -1;
+	}
+
+	memarea_perftest_clear_env();
+	return 0;
+}
+
 static int
 test_malloc_perf(void)
 {
@@ -168,6 +245,11 @@ test_malloc_perf(void)
 			NULL, memset_us_gb, RTE_MAX_MEMZONE - 1) < 0)
 		return -1;
 
+	if (memarea_perftest(RTE_MEMAREA_SOURCE_HEAP, memset_us_gb, MAX_RUNS) < 0)
+		return -1;
+	if (memarea_perftest(RTE_MEMAREA_SOURCE_LIBC, memset_us_gb, MAX_RUNS) < 0)
+		return -1;
+
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v11 0/6] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (14 preceding siblings ...)
  2022-10-17  3:40 ` [PATCH v10 0/7] introduce memarea library Chengwen Feng
@ 2022-12-13  9:13 ` Chengwen Feng
  2022-12-13  9:13   ` [PATCH v11 1/6] memarea: " Chengwen Feng
                     ` (6 more replies)
  2023-01-14 11:49 ` [PATCH v12 " Chengwen Feng
                   ` (7 subsequent siblings)
  23 siblings, 7 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-12-13  9:13 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- 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 from another memarea.

- It provides refcnt feature which could be useful in multi-reader
  scenario.

- It supports MT-safe as long as it's specified at creation time.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>

Chengwen Feng (6):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc/free/refcnt-update API
  test/memarea: support alloc/free/refcnt-update test
  memarea: support dump API
  test/memarea: support dump test

---
v11:
* rebase 23.03
* remove "app/test: add memarea to malloc-perf-autotest" because the
  two algorithm are not comparable which also address previous 
  comments.
v10:
* support windows platform.
* add rte_memarea.libc perftest to malloc-perf-autotest.
v9:
* address Dmitry's comments.
* drop features of SOURCE_USER and backup memarea mechanism.
* rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
  to keep with rte_mbuf_refcnt_update name style.
* fix memarea perftest compile failed at windows platform.
* fix spell warning.
v8:
* address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
* small feature patches are combined.
* enhanced backup memory mechanism.
* add memarea to malloc-perf-autotest.
* other tiny naming optimize.
v7:
* repost patches as there are spread over different series in patchwork.
v6:
* address Mattias's comments.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 308 ++++++++++++++++++
 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  |  52 ++++
 doc/guides/rel_notes/release_23_03.rst |   5 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  36 +++
 lib/memarea/meson.build                |  10 +
 lib/memarea/rte_memarea.c              | 413 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 219 +++++++++++++
 lib/memarea/version.map                |  16 +
 lib/meson.build                        |   1 +
 16 files changed, 1074 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v11 1/6] memarea: introduce memarea library
  2022-12-13  9:13 ` [PATCH v11 0/6] introduce memarea library Chengwen Feng
@ 2022-12-13  9:13   ` Chengwen Feng
  2022-12-13  9:13   ` [PATCH v11 2/6] test/memarea: support memarea test Chengwen Feng
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-12-13  9:13 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

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>
---
 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  |  39 ++++++
 doc/guides/rel_notes/release_23_03.rst |   5 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  33 ++++++
 lib/memarea/meson.build                |  10 ++
 lib/memarea/rte_memarea.c              | 158 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 124 +++++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 14 files changed, 393 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 3dc3f5b348..c81a02c78e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1576,6 +1576,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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index de488c7abf..24456604f8 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -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),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index f0886c3bd1..8334ebcbd6 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -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 \
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..d032b24ce9
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -0,0 +1,39 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2022 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 provides refcnt feature which could be useful in multi-reader scenario.
+
+* 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.
+
+Reference
+---------
+
+[1] https://en.wikipedia.org/wiki/Region-based_memory_management
diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index b8c5b68d6c..052603dffd 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -55,6 +55,11 @@ 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.
 
 Removed Items
 -------------
diff --git a/lib/eal/common/eal_common_log.c b/lib/eal/common/eal_common_log.c
index bd7b188ceb..3d62af59c6 100644
--- a/lib/eal/common/eal_common_log.c
+++ b/lib/eal/common/eal_common_log.c
@@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
 	{RTE_LOGTYPE_EFD,        "lib.efd"},
 	{RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
 	{RTE_LOGTYPE_GSO,        "lib.gso"},
+	{RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
 	{RTE_LOGTYPE_USER1,      "user1"},
 	{RTE_LOGTYPE_USER2,      "user2"},
 	{RTE_LOGTYPE_USER3,      "user3"},
diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
index 6d2b0856a5..9dc6fb593c 100644
--- a/lib/eal/include/rte_log.h
+++ b/lib/eal/include/rte_log.h
@@ -47,6 +47,7 @@ extern "C" {
 #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
 #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
 #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
+#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..59be9c1d00
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#ifndef MEMAREA_PRIVATE_H
+#define MEMAREA_PRIVATE_H
+
+#include <rte_memarea.h>
+
+#define MEMAREA_AVAILABLE_ELEM_MAGIC	0xbeef1234
+#define MEMAREA_ALLOCATED_ELEM_MAGIC	0xbeef1230
+#define MEMAREA_AVAILABLE_ELEM_COOKIE	0xffffffff
+
+struct memarea_elem {
+	TAILQ_ENTRY(memarea_elem) elem_node;
+	TAILQ_ENTRY(memarea_elem) free_node;
+	size_t   size;
+	uint32_t magic;
+	uint32_t cookie;
+	int32_t  refcnt; /* Non-zero indicates that it has been allocated */
+} __rte_cache_aligned;
+
+TAILQ_HEAD(memarea_elem_list, memarea_elem);
+
+struct rte_memarea {
+	struct rte_memarea_param init;
+	rte_spinlock_t           lock;
+	void                    *area_addr;
+	struct memarea_elem_list elem_list;
+	struct memarea_elem_list free_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..d5c2c442f5
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2022 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..41e5c007c2
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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"
+
+static int
+memarea_check_param(const struct rte_memarea_param *init)
+{
+#define MEMAREA_MIN_SIZE	1024
+	size_t len;
+
+	if (init == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
+		return -EINVAL;
+	}
+
+	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
+	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
+		return -EINVAL;
+	}
+
+	if (init->source != RTE_MEMAREA_SOURCE_HEAP &&
+	    init->source != RTE_MEMAREA_SOURCE_LIBC &&
+	    init->source != RTE_MEMAREA_SOURCE_MEMAREA) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
+			init->name, init->source);
+		return -EINVAL;
+	}
+
+	if (init->total_sz < MEMAREA_MIN_SIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
+			init->name, init->total_sz);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_MEMAREA && init->src_memarea == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source memarea is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->alg != RTE_MEMAREA_ALG_NEXTFIT) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alg: %d not supported!\n",
+			init->name, init->alg);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+memarea_alloc_from_libc(size_t size)
+{
+#ifndef RTE_EXEC_ENV_WINDOWS
+	void *ptr = NULL;
+	int ret;
+	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+	if (ret)
+		return NULL;
+	return ptr;
+#else
+	/* Windows platform don't support posix_memalign() */
+	return malloc(size);
+#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);
+
+	if (ptr == NULL)
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
+
+	return ptr;
+}
+
+struct rte_memarea *
+rte_memarea_create(const struct rte_memarea_param *init)
+{
+	struct memarea_elem *elem;
+	struct rte_memarea *ma;
+	size_t unaligns;
+	void *addr;
+	int ret;
+
+	ret = memarea_check_param(init);
+	if (ret)
+		return NULL;
+
+	addr = memarea_alloc_area(init);
+	if (addr == NULL)
+		return NULL;
+
+	ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
+	if (ma == NULL) {
+		RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
+		return NULL;
+	}
+
+	ma->init = *init;
+	rte_spinlock_init(&ma->lock);
+	TAILQ_INIT(&ma->elem_list);
+	TAILQ_INIT(&ma->free_list);
+	ma->area_addr = addr;
+	/* The address returned by the windows platform may not be cache-line aligned.
+	 * The elem address and total_sz are adjusted.
+	 */
+	unaligns = ((uintptr_t)addr) & (RTE_CACHE_LINE_SIZE - 1);
+	if (unaligns > 0) {
+		elem = RTE_PTR_ADD(addr, RTE_CACHE_LINE_SIZE - unaligns);
+		ma->init.total_sz -= (RTE_CACHE_LINE_SIZE - unaligns);
+	} else {
+		elem = addr;
+	}
+	TAILQ_INSERT_TAIL(&ma->elem_list, elem, elem_node);
+	TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	elem->size = init->total_sz - sizeof(struct memarea_elem);
+	elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+	elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+	elem->refcnt = 0;
+
+	return ma;
+}
+
+static void
+memarea_free_area(struct rte_memarea *ma)
+{
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		rte_free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
+		free(ma->area_addr);
+}
+
+void
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+	if (ma == NULL)
+		return;
+	memarea_free_area(ma);
+	rte_free(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
new file mode 100644
index 0000000000..d48ceaf733
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 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 provides refcnt feature which could be useful in multi-reader scenario.
+ *
+ * - 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_alg {
+	/** The default management algorithm is a variant of the next fit
+	 * algorithm. It uses a free-list to apply for memory and uses an
+	 * element-list in ascending order of address to support merging
+	 * upon free.
+	 */
+	RTE_MEMAREA_ALG_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_alg 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_memarea;
+	};
+};
+
+/**
+ * @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 */
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 a90fee31b7..f561875305 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v11 2/6] test/memarea: support memarea test
  2022-12-13  9:13 ` [PATCH v11 0/6] introduce memarea library Chengwen Feng
  2022-12-13  9:13   ` [PATCH v11 1/6] memarea: " Chengwen Feng
@ 2022-12-13  9:13   ` Chengwen Feng
  2022-12-13  9:13   ` [PATCH v11 3/6] memarea: support alloc/free/refcnt-update API Chengwen Feng
                     ` (4 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-12-13  9:13 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports memarea test of rte_memarea_create() and
rte_memarea_destroy() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 130 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index c81a02c78e..aa5743036d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1580,6 +1580,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index f34d19e3c3..fde0155cf9 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -84,6 +84,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -200,6 +201,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..cfd50de45f
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) { \
+			printf("%s Failed\n", #test_func); \
+			fails++; \
+		} else { \
+			printf("%s Passed\n", #test_func); \
+		} \
+	} while (0)
+
+static int fails;
+
+static void
+test_memarea_prepare(void)
+{
+	fails = 0;
+}
+
+static int
+test_memarea_retcode(void)
+{
+	return fails > 0 ? -1 : 0;
+}
+
+static void
+test_memarea_init_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "test-memarea");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for memarea NULL */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for alg invalid */
+	test_memarea_init_param(&init);
+	init.alg = RTE_MEMAREA_ALG_NEXTFIT + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for create with HEAP */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.numa_socket = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	test_memarea_prepare();
+
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+
+	return test_memarea_retcode();
+}
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v11 3/6] memarea: support alloc/free/refcnt-update API
  2022-12-13  9:13 ` [PATCH v11 0/6] introduce memarea library Chengwen Feng
  2022-12-13  9:13   ` [PATCH v11 1/6] memarea: " Chengwen Feng
  2022-12-13  9:13   ` [PATCH v11 2/6] test/memarea: support memarea test Chengwen Feng
@ 2022-12-13  9:13   ` Chengwen Feng
  2023-01-10  2:16     ` Dongdong Liu
  2022-12-13  9:13   ` [PATCH v11 4/6] test/memarea: support alloc/free/refcnt-update test Chengwen Feng
                     ` (3 subsequent siblings)
  6 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2022-12-13  9:13 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_refcnt_update() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  10 ++
 lib/memarea/memarea_private.h         |   3 +
 lib/memarea/rte_memarea.c             | 157 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  74 ++++++++++++
 lib/memarea/version.map               |   3 +
 5 files changed, 247 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index d032b24ce9..8fbfd06a5d 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,16 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
+The ``rte_memarea_refcnt_update()`` function is used to update the memory
+object's reference count, if the count reaches zero, the memory object will
+be freed to memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 59be9c1d00..f5accf2987 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -28,6 +28,9 @@ struct rte_memarea {
 	void                    *area_addr;
 	struct memarea_elem_list elem_list;
 	struct memarea_elem_list free_list;
+
+	uint64_t alloc_fails;
+	uint64_t refcnt_check_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 41e5c007c2..a0935dafb6 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -4,6 +4,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -84,6 +85,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 					init->numa_socket);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		ptr = memarea_alloc_from_libc(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		ptr = rte_memarea_alloc(init->src_memarea, init->total_sz, 0);
 
 	if (ptr == NULL)
 		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
@@ -146,6 +149,8 @@ memarea_free_area(struct rte_memarea *ma)
 		rte_free(ma->area_addr);
 	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ma->area_addr);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		rte_memarea_free(ma->init.src_memarea, ma->area_addr);
 }
 
 void
@@ -156,3 +161,155 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(ma);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+static inline bool
+memarea_whether_add_node(size_t free_size, size_t need_size)
+{
+	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
+	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
+}
+
+static inline void
+memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
+{
+	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
+	struct memarea_elem *new_elem;
+
+	new_elem = (struct memarea_elem *)RTE_PTR_ADD(elem, sizeof(struct memarea_elem) +
+							    align_size);
+	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
+	new_elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+	new_elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+	new_elem->refcnt = 0;
+	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
+	TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
+	elem->size = align_size;
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
+{
+	struct memarea_elem *elem;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0))
+		return NULL;
+
+	memarea_lock(ma);
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (unlikely(elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC))
+			break;
+		if (elem->size < size)
+			continue;
+		if (memarea_whether_add_node(elem->size, size))
+			memarea_add_node(ma, elem, size);
+		elem->magic = MEMAREA_ALLOCATED_ELEM_MAGIC;
+		elem->cookie = cookie;
+		elem->refcnt = 1;
+		TAILQ_REMOVE(&ma->free_list, elem, free_node);
+		ptr = RTE_PTR_ADD(elem, sizeof(struct memarea_elem));
+		break;
+	}
+	if (unlikely(ptr == NULL))
+		ma->alloc_fails++;
+	memarea_unlock(ma);
+
+	return ptr;
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	if (unlikely(ma == NULL || ptr == NULL))
+		return;
+
+	rte_memarea_refcnt_update(ma, ptr, -1);
+}
+
+static inline void
+memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
+		   struct memarea_elem *next, bool del_next_from_free,
+		   bool add_curr_to_free)
+{
+	curr->size += next->size + sizeof(struct memarea_elem);
+	next->size = 0;
+	next->magic = 0;
+	next->cookie = 0;
+	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
+	if (del_next_from_free)
+		TAILQ_REMOVE(&ma->free_list, next, free_node);
+	if (add_curr_to_free) {
+		curr->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+		curr->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
+	}
+}
+
+static inline void
+memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
+{
+	struct memarea_elem *prev, *next;
+	bool merged = false;
+
+	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
+	next = TAILQ_NEXT(elem, elem_node);
+	if (prev != NULL && prev->refcnt == 0) {
+		memarea_merge_node(ma, prev, elem, false, false);
+		elem = prev;
+		merged = true;
+	}
+	if (next != NULL && next->refcnt == 0) {
+		memarea_merge_node(ma, elem, next, true, !merged);
+		merged = true;
+	}
+	if (!merged) {
+		elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
+		elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
+		TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
+	}
+}
+
+void
+rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value)
+{
+	struct memarea_elem *elem = (struct memarea_elem *)RTE_PTR_SUB(ptr,
+							    sizeof(struct memarea_elem));
+
+	memarea_lock(ma);
+	if (unlikely(elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC)) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail!\n",
+			ma->init.name, elem->magic);
+		memarea_unlock(ma);
+		return;
+	}
+
+	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
+		RTE_LOG(ERR, MEMAREA,
+			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
+			ma->init.name, elem->cookie, elem->refcnt, value);
+		ma->refcnt_check_fails++;
+		if (elem->refcnt > 0)
+			elem->refcnt += value;
+		memarea_unlock(ma);
+		return;
+	}
+
+	elem->refcnt += value;
+	if (elem->refcnt == 0)
+		memarea_free_elem(ma, elem);
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index d48ceaf733..31c499ec07 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -117,6 +117,80 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ * @param cookie
+ *   User-provided footprint which could used to debug memory leak.
+ *
+ * @return
+ *   - NULL on error. Not enough memory, or invalid arguments (ma is NULL,
+ *     size is 0).
+ *   - Otherwise, the pointer to the allocated object.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea. If the object's refcnt is greater
+ * than one, only the refcnt is decremented by one. Otherwise the object is
+ * released.
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), if it is allocated from memarea-A, it must be freed to
+ * the same memarea-A. The behaviour of rte_memarea_free() is undefined if the
+ * memarea or pointer does not match these requirements.
+ *
+ * @param ma
+ *   The pointer of memarea. If the ma is NULL, the function does nothing.
+ * @param ptr
+ *   The pointer of memory object which need be freed. If the pointer is NULL,
+ *   the function does nothing.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Update memory's refcnt.
+ *
+ * Update one memory object's refcnt. When refcnt is updated to be zero, the
+ * memory object is freed.
+ *
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), if it is allocated from memarea-A, the refcnt update
+ * must be operated to the same memarea-A. The behaviour of
+ * rte_memarea_refcnt_update() is undefined if the memarea or pointer does not
+ * match these requirements.
+ *
+ * @note If the memory object's refcnt updated to be lower than zero, an error
+ * message will be printed, and the memory object will not freed to memrea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param ptr
+ *   The pointer of memory object which need be updated refcnt.
+ * @param value
+ *   The value which need be updated.
+ */
+__rte_experimental
+void rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..a087d40d5f 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,11 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
+	rte_memarea_refcnt_update;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v11 4/6] test/memarea: support alloc/free/refcnt-update test
  2022-12-13  9:13 ` [PATCH v11 0/6] introduce memarea library Chengwen Feng
                     ` (2 preceding siblings ...)
  2022-12-13  9:13   ` [PATCH v11 3/6] memarea: support alloc/free/refcnt-update API Chengwen Feng
@ 2022-12-13  9:13   ` Chengwen Feng
  2022-12-13  9:13   ` [PATCH v11 5/6] memarea: support dump API Chengwen Feng
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-12-13  9:13 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_alloc()/rte_memarea_free()/
rte_memarea_refcnt_update() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 145 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 144 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index cfd50de45f..00f9d07ed4 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -47,6 +47,12 @@ test_memarea_init_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -95,8 +101,8 @@ test_memarea_create_bad_param(void)
 static int
 test_memarea_create_destroy(void)
 {
+	struct rte_memarea *ma, *src_ma;
 	struct rte_memarea_param init;
-	struct rte_memarea *ma;
 
 	/* test for create with HEAP */
 	test_memarea_init_param(&init);
@@ -113,6 +119,140 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with another memarea */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	src_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(src_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.src_memarea = src_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(src_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE - RTE_CACHE_LINE_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	ptr[1] = rte_memarea_alloc(ma, 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test free with refcnt fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	/* test update refcnt with fail */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_refcnt_update(ma, ptr, -2);
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr == NULL, "Expected NULL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[ALLOC_MAX_NUM];
+	int i;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1, 0);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, NULL);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -123,6 +263,9 @@ test_memarea(void)
 
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v11 5/6] memarea: support dump API
  2022-12-13  9:13 ` [PATCH v11 0/6] introduce memarea library Chengwen Feng
                     ` (3 preceding siblings ...)
  2022-12-13  9:13   ` [PATCH v11 4/6] test/memarea: support alloc/free/refcnt-update test Chengwen Feng
@ 2022-12-13  9:13   ` Chengwen Feng
  2022-12-13  9:13   ` [PATCH v11 6/6] test/memarea: support dump test Chengwen Feng
  2023-01-10  2:21   ` [PATCH v11 0/6] introduce memarea library Dongdong Liu
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-12-13  9:13 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +
 lib/memarea/rte_memarea.c             | 98 +++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 21 ++++++
 lib/memarea/version.map               |  1 +
 4 files changed, 123 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 8fbfd06a5d..c33a548417 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -43,6 +43,9 @@ The ``rte_memarea_refcnt_update()`` function is used to update the memory
 object's reference count, if the count reaches zero, the memory object will
 be freed to memarea.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index a0935dafb6..c58f68df53 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,6 +2,7 @@
  * Copyright(c) 2022 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/queue.h>
@@ -313,3 +314,100 @@ rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value)
 		memarea_free_elem(ma, elem);
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_MEMAREA)
+		return "memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_alg alg)
+{
+	if (alg == RTE_MEMAREA_ALG_NEXTFIT)
+		return "nextfit";
+	else
+		return "unknown";
+}
+
+static uint32_t
+memarea_elem_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC &&
+		    elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC)
+			break;
+		num++;
+	}
+
+	return num;
+}
+
+static uint32_t
+memarea_free_list_num(struct rte_memarea *ma)
+{
+	struct memarea_elem *elem;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
+		if (elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC)
+			break;
+		num++;
+	}
+
+	return num;
+}
+
+static void
+memarea_dump_all(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_elem *elem;
+
+	fprintf(f, "  regions:\n");
+	TAILQ_FOREACH(elem, &ma->elem_list, elem_node) {
+		if (elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC &&
+		    elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC) {
+			fprintf(f, "    magic: 0x%x check fail!\n", elem->magic);
+			break;
+		}
+		fprintf(f, "    size: 0x%zx cookie: 0x%x refcnt: %d\n",
+			elem->size, elem->cookie, elem->refcnt);
+	}
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		fprintf(f, "  heap-numa-socket: %d\n", ma->init.numa_socket);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		fprintf(f, "  source-memarea: %s\n", ma->init.src_memarea->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	fprintf(f, "  total-regions: %u\n", memarea_elem_list_num(ma));
+	fprintf(f, "  total-free-regions: %u\n", memarea_free_list_num(ma));
+	fprintf(f, "  alloc_fails: %" PRIu64 "\n", ma->alloc_fails);
+	fprintf(f, "  refcnt_check_fails: %" PRIu64 "\n", ma->refcnt_check_fails);
+	if (dump_all)
+		memarea_dump_all(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 31c499ec07..61042a8d0d 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -191,6 +191,27 @@ void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 __rte_experimental
 void rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index a087d40d5f..6d75f9f7c4 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 	rte_memarea_refcnt_update;
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v11 6/6] test/memarea: support dump test
  2022-12-13  9:13 ` [PATCH v11 0/6] introduce memarea library Chengwen Feng
                     ` (4 preceding siblings ...)
  2022-12-13  9:13   ` [PATCH v11 5/6] memarea: support dump API Chengwen Feng
@ 2022-12-13  9:13   ` Chengwen Feng
  2023-01-10  2:21   ` [PATCH v11 0/6] introduce memarea library Dongdong Liu
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2022-12-13  9:13 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_dump() test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
 app/test/test_memarea.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 00f9d07ed4..da712a14e8 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -256,6 +256,40 @@ test_memarea_alloc_free(void)
 	return 0;
 }
 
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	/* prepare env */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -266,6 +300,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v11 3/6] memarea: support alloc/free/refcnt-update API
  2022-12-13  9:13   ` [PATCH v11 3/6] memarea: support alloc/free/refcnt-update API Chengwen Feng
@ 2023-01-10  2:16     ` Dongdong Liu
  0 siblings, 0 replies; 222+ messages in thread
From: Dongdong Liu @ 2023-01-10  2:16 UTC (permalink / raw)
  To: Chengwen Feng, david.marchand, mb, anatoly.burakov,
	dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev



On 2022/12/13 17:13, Chengwen Feng wrote:
> This patch supports rte_memarea_alloc()/rte_memarea_free()/
> rte_memarea_refcnt_update() API.
>
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> ---
>  doc/guides/prog_guide/memarea_lib.rst |  10 ++
>  lib/memarea/memarea_private.h         |   3 +
>  lib/memarea/rte_memarea.c             | 157 ++++++++++++++++++++++++++
>  lib/memarea/rte_memarea.h             |  74 ++++++++++++
>  lib/memarea/version.map               |   3 +
>  5 files changed, 247 insertions(+)
>
> diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
> index d032b24ce9..8fbfd06a5d 100644
> --- a/doc/guides/prog_guide/memarea_lib.rst
> +++ b/doc/guides/prog_guide/memarea_lib.rst
> @@ -33,6 +33,16 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
>
>  The ``rte_memarea_destroy()`` function is used to destroy a memarea.
>
> +The ``rte_memarea_alloc()`` function is used to alloc one memory object from
> +the memarea.
> +
> +The ``rte_memarea_free()`` function is used to free one memory object which
> +allocated by ``rte_memarea_alloc()``.
> +
> +The ``rte_memarea_refcnt_update()`` function is used to update the memory
> +object's reference count, if the count reaches zero, the memory object will
> +be freed to memarea.
It seems provide rte_memarea_refcnt_update() API is too usage-specific.
can we delete this ?

Thanks,
Dongdong
> +
>  Reference
>  ---------
>
> diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
> index 59be9c1d00..f5accf2987 100644
> --- a/lib/memarea/memarea_private.h
> +++ b/lib/memarea/memarea_private.h
> @@ -28,6 +28,9 @@ struct rte_memarea {
>  	void                    *area_addr;
>  	struct memarea_elem_list elem_list;
>  	struct memarea_elem_list free_list;
> +
> +	uint64_t alloc_fails;
> +	uint64_t refcnt_check_fails;
>  } __rte_cache_aligned;
>
>  #endif /* MEMAREA_PRIVATE_H */
> diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
> index 41e5c007c2..a0935dafb6 100644
> --- a/lib/memarea/rte_memarea.c
> +++ b/lib/memarea/rte_memarea.c
> @@ -4,6 +4,7 @@
>
>  #include <stdio.h>
>  #include <stdlib.h>
> +#include <sys/queue.h>
>
>  #include <rte_common.h>
>  #include <rte_log.h>
> @@ -84,6 +85,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
>  					init->numa_socket);
>  	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
>  		ptr = memarea_alloc_from_libc(init->total_sz);
> +	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
> +		ptr = rte_memarea_alloc(init->src_memarea, init->total_sz, 0);
>
>  	if (ptr == NULL)
>  		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
> @@ -146,6 +149,8 @@ memarea_free_area(struct rte_memarea *ma)
>  		rte_free(ma->area_addr);
>  	else if (ma->init.source == RTE_MEMAREA_SOURCE_LIBC)
>  		free(ma->area_addr);
> +	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
> +		rte_memarea_free(ma->init.src_memarea, ma->area_addr);
>  }
>
>  void
> @@ -156,3 +161,155 @@ rte_memarea_destroy(struct rte_memarea *ma)
>  	memarea_free_area(ma);
>  	rte_free(ma);
>  }
> +
> +static inline void
> +memarea_lock(struct rte_memarea *ma)
> +{
> +	if (ma->init.mt_safe)
> +		rte_spinlock_lock(&ma->lock);
> +}
> +
> +static inline void
> +memarea_unlock(struct rte_memarea *ma)
> +{
> +	if (ma->init.mt_safe)
> +		rte_spinlock_unlock(&ma->lock);
> +}
> +
> +static inline bool
> +memarea_whether_add_node(size_t free_size, size_t need_size)
> +{
> +	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
> +	return free_size > align_size && (free_size - align_size) > sizeof(struct memarea_elem);
> +}
> +
> +static inline void
> +memarea_add_node(struct rte_memarea *ma, struct memarea_elem *elem, size_t need_size)
> +{
> +	size_t align_size = RTE_ALIGN_CEIL(need_size, RTE_CACHE_LINE_SIZE);
> +	struct memarea_elem *new_elem;
> +
> +	new_elem = (struct memarea_elem *)RTE_PTR_ADD(elem, sizeof(struct memarea_elem) +
> +							    align_size);
> +	new_elem->size = elem->size - align_size - sizeof(struct memarea_elem);
> +	new_elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
> +	new_elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
> +	new_elem->refcnt = 0;
> +	TAILQ_INSERT_AFTER(&ma->elem_list, elem, new_elem, elem_node);
> +	TAILQ_INSERT_AFTER(&ma->free_list, elem, new_elem, free_node);
> +	elem->size = align_size;
> +}
> +
> +void *
> +rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie)
> +{
> +	struct memarea_elem *elem;
> +	void *ptr = NULL;
> +
> +	if (unlikely(ma == NULL || size == 0))
> +		return NULL;
> +
> +	memarea_lock(ma);
> +	TAILQ_FOREACH(elem, &ma->free_list, free_node) {
> +		if (unlikely(elem->magic != MEMAREA_AVAILABLE_ELEM_MAGIC))
> +			break;
> +		if (elem->size < size)
> +			continue;
> +		if (memarea_whether_add_node(elem->size, size))
> +			memarea_add_node(ma, elem, size);
> +		elem->magic = MEMAREA_ALLOCATED_ELEM_MAGIC;
> +		elem->cookie = cookie;
> +		elem->refcnt = 1;
> +		TAILQ_REMOVE(&ma->free_list, elem, free_node);
> +		ptr = RTE_PTR_ADD(elem, sizeof(struct memarea_elem));
> +		break;
> +	}
> +	if (unlikely(ptr == NULL))
> +		ma->alloc_fails++;
> +	memarea_unlock(ma);
> +
> +	return ptr;
> +}
> +
> +void
> +rte_memarea_free(struct rte_memarea *ma, void *ptr)
> +{
> +	if (unlikely(ma == NULL || ptr == NULL))
> +		return;
> +
> +	rte_memarea_refcnt_update(ma, ptr, -1);
> +}
> +
> +static inline void
> +memarea_merge_node(struct rte_memarea *ma, struct memarea_elem *curr,
> +		   struct memarea_elem *next, bool del_next_from_free,
> +		   bool add_curr_to_free)
> +{
> +	curr->size += next->size + sizeof(struct memarea_elem);
> +	next->size = 0;
> +	next->magic = 0;
> +	next->cookie = 0;
> +	TAILQ_REMOVE(&ma->elem_list, next, elem_node);
> +	if (del_next_from_free)
> +		TAILQ_REMOVE(&ma->free_list, next, free_node);
> +	if (add_curr_to_free) {
> +		curr->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
> +		curr->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
> +		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
> +	}
> +}
> +
> +static inline void
> +memarea_free_elem(struct rte_memarea *ma, struct memarea_elem *elem)
> +{
> +	struct memarea_elem *prev, *next;
> +	bool merged = false;
> +
> +	prev = TAILQ_PREV(elem, memarea_elem_list, elem_node);
> +	next = TAILQ_NEXT(elem, elem_node);
> +	if (prev != NULL && prev->refcnt == 0) {
> +		memarea_merge_node(ma, prev, elem, false, false);
> +		elem = prev;
> +		merged = true;
> +	}
> +	if (next != NULL && next->refcnt == 0) {
> +		memarea_merge_node(ma, elem, next, true, !merged);
> +		merged = true;
> +	}
> +	if (!merged) {
> +		elem->magic = MEMAREA_AVAILABLE_ELEM_MAGIC;
> +		elem->cookie = MEMAREA_AVAILABLE_ELEM_COOKIE;
> +		TAILQ_INSERT_TAIL(&ma->free_list, elem, free_node);
> +	}
> +}
> +
> +void
> +rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value)
> +{
> +	struct memarea_elem *elem = (struct memarea_elem *)RTE_PTR_SUB(ptr,
> +							    sizeof(struct memarea_elem));
> +
> +	memarea_lock(ma);
> +	if (unlikely(elem->magic != MEMAREA_ALLOCATED_ELEM_MAGIC)) {
> +		RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail!\n",
> +			ma->init.name, elem->magic);
> +		memarea_unlock(ma);
> +		return;
> +	}
> +
> +	if (unlikely(elem->refcnt <= 0 || elem->refcnt + value < 0)) {
> +		RTE_LOG(ERR, MEMAREA,
> +			"memarea: %s cookie: 0x%x curr refcnt: %d update refcnt: %d check fail!\n",
> +			ma->init.name, elem->cookie, elem->refcnt, value);
> +		ma->refcnt_check_fails++;
> +		if (elem->refcnt > 0)
> +			elem->refcnt += value;
> +		memarea_unlock(ma);
> +		return;
> +	}
> +
> +	elem->refcnt += value;
> +	if (elem->refcnt == 0)
> +		memarea_free_elem(ma, elem);
> +	memarea_unlock(ma);
> +}
> diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
> index d48ceaf733..31c499ec07 100644
> --- a/lib/memarea/rte_memarea.h
> +++ b/lib/memarea/rte_memarea.h
> @@ -117,6 +117,80 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
>  __rte_experimental
>  void rte_memarea_destroy(struct rte_memarea *ma);
>
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Allocate memory from memarea.
> + *
> + * Allocate one memory object from the memarea.
> + *
> + * @param ma
> + *   The pointer of memarea.
> + * @param size
> + *   The memory size to be allocated.
> + * @param cookie
> + *   User-provided footprint which could used to debug memory leak.
> + *
> + * @return
> + *   - NULL on error. Not enough memory, or invalid arguments (ma is NULL,
> + *     size is 0).
> + *   - Otherwise, the pointer to the allocated object.
> + */
> +__rte_experimental
> +void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t cookie);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Free memory to memarea.
> + *
> + * Free one memory object to the memarea. If the object's refcnt is greater
> + * than one, only the refcnt is decremented by one. Otherwise the object is
> + * released.
> + * @note The memory object must have been returned by a previous call to
> + * rte_memarea_alloc(), if it is allocated from memarea-A, it must be freed to
> + * the same memarea-A. The behaviour of rte_memarea_free() is undefined if the
> + * memarea or pointer does not match these requirements.
> + *
> + * @param ma
> + *   The pointer of memarea. If the ma is NULL, the function does nothing.
> + * @param ptr
> + *   The pointer of memory object which need be freed. If the pointer is NULL,
> + *   the function does nothing.
> + */
> +__rte_experimental
> +void rte_memarea_free(struct rte_memarea *ma, void *ptr);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * Update memory's refcnt.
> + *
> + * Update one memory object's refcnt. When refcnt is updated to be zero, the
> + * memory object is freed.
> + *
> + * @note The memory object must have been returned by a previous call to
> + * rte_memarea_alloc(), if it is allocated from memarea-A, the refcnt update
> + * must be operated to the same memarea-A. The behaviour of
> + * rte_memarea_refcnt_update() is undefined if the memarea or pointer does not
> + * match these requirements.
> + *
> + * @note If the memory object's refcnt updated to be lower than zero, an error
> + * message will be printed, and the memory object will not freed to memrea.
> + *
> + * @param ma
> + *   The pointer of memarea.
> + * @param ptr
> + *   The pointer of memory object which need be updated refcnt.
> + * @param value
> + *   The value which need be updated.
> + */
> +__rte_experimental
> +void rte_memarea_refcnt_update(struct rte_memarea *ma, void *ptr, int32_t value);
> +
>  #ifdef __cplusplus
>  }
>  #endif
> diff --git a/lib/memarea/version.map b/lib/memarea/version.map
> index f36a04d7cf..a087d40d5f 100644
> --- a/lib/memarea/version.map
> +++ b/lib/memarea/version.map
> @@ -1,8 +1,11 @@
>  EXPERIMENTAL {
>  	global:
>
> +	rte_memarea_alloc;
>  	rte_memarea_create;
>  	rte_memarea_destroy;
> +	rte_memarea_free;
> +	rte_memarea_refcnt_update;
>
>  	local: *;
>  };
>

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v11 0/6] introduce memarea library
  2022-12-13  9:13 ` [PATCH v11 0/6] introduce memarea library Chengwen Feng
                     ` (5 preceding siblings ...)
  2022-12-13  9:13   ` [PATCH v11 6/6] test/memarea: support dump test Chengwen Feng
@ 2023-01-10  2:21   ` Dongdong Liu
  6 siblings, 0 replies; 222+ messages in thread
From: Dongdong Liu @ 2023-01-10  2:21 UTC (permalink / raw)
  To: Chengwen Feng, david.marchand, mb, anatoly.burakov,
	dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

Hi Chengwen

Except for a minor comments, this patchset looks good to me.
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>

Thanks,
Dongdong
On 2022/12/13 17:13, Chengwen Feng wrote:
> The memarea library is an allocator of variable-size object which based
> on a memory region. The main features are as follows:
>
> - 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 from another memarea.
>
> - It provides refcnt feature which could be useful in multi-reader
>   scenario.
>
> - It supports MT-safe as long as it's specified at creation time.
>
> Note:
> a) The memarea is oriented towards the application layer, which could
> provides 'region-based memory management' [1] function.
> b) The eal library also provide memory zone/heap management, but these
> are tied to huge pages management.
>
> [1] https://en.wikipedia.org/wiki/Region-based_memory_management
>
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
>
> Chengwen Feng (6):
>   memarea: introduce memarea library
>   test/memarea: support memarea test
>   memarea: support alloc/free/refcnt-update API
>   test/memarea: support alloc/free/refcnt-update test
>   memarea: support dump API
>   test/memarea: support dump test
>
> ---
> v11:
> * rebase 23.03
> * remove "app/test: add memarea to malloc-perf-autotest" because the
>   two algorithm are not comparable which also address previous
>   comments.
> v10:
> * support windows platform.
> * add rte_memarea.libc perftest to malloc-perf-autotest.
> v9:
> * address Dmitry's comments.
> * drop features of SOURCE_USER and backup memarea mechanism.
> * rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
>   to keep with rte_mbuf_refcnt_update name style.
> * fix memarea perftest compile failed at windows platform.
> * fix spell warning.
> v8:
> * address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
> * small feature patches are combined.
> * enhanced backup memory mechanism.
> * add memarea to malloc-perf-autotest.
> * other tiny naming optimize.
> v7:
> * repost patches as there are spread over different series in patchwork.
> v6:
> * address Mattias's comments.
> v5:
> * fix 09/10 patch spell warning.
> v4:
> * repost patches as there are spread over different series in patchwork.
> v3:
> * add memory source of RTE memory.
> * add algorithm field to facilitate the introduction of new algorithms.
> * fix memarea log don't output problem.
> v2:
> * fix compile issues reported by dpdk-test-report.
> * address Dimitry and Jerin's comments.
> * add no MT-safe test.
>
>  MAINTAINERS                            |   6 +
>  app/test/meson.build                   |   2 +
>  app/test/test_memarea.c                | 308 ++++++++++++++++++
>  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  |  52 ++++
>  doc/guides/rel_notes/release_23_03.rst |   5 +
>  lib/eal/common/eal_common_log.c        |   1 +
>  lib/eal/include/rte_log.h              |   1 +
>  lib/memarea/memarea_private.h          |  36 +++
>  lib/memarea/meson.build                |  10 +
>  lib/memarea/rte_memarea.c              | 413 +++++++++++++++++++++++++
>  lib/memarea/rte_memarea.h              | 219 +++++++++++++
>  lib/memarea/version.map                |  16 +
>  lib/meson.build                        |   1 +
>  16 files changed, 1074 insertions(+), 1 deletion(-)
>  create mode 100644 app/test/test_memarea.c
>  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
>

^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v12 0/6] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (15 preceding siblings ...)
  2022-12-13  9:13 ` [PATCH v11 0/6] introduce memarea library Chengwen Feng
@ 2023-01-14 11:49 ` Chengwen Feng
  2023-01-14 11:49   ` [PATCH v12 1/6] memarea: " Chengwen Feng
                     ` (5 more replies)
  2023-02-08  8:24 ` [PATCH v13 0/6] introduce memarea library Chengwen Feng
                   ` (6 subsequent siblings)
  23 siblings, 6 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-01-14 11:49 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk,
	hofors, liudongdong3
  Cc: thomas, dev

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- 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 from another memarea.

- It supports MT-safe as long as it's specified at creation time.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Chengwen Feng (6):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc and free API
  test/memarea: support alloc and free API test
  memarea: support dump API
  test/memarea: support dump API test

---
v12:
* remove rte_memarea_refcnt_update() API which address Dongdong's 
  comments.
* refine the variable naming.
* fix some bugs.
v11:
* rebase 23.03
* remove "app/test: add memarea to malloc-perf-autotest" because the
  two algorithm are not comparable which also address previous
  comments.
v10:
* support windows platform.
* add rte_memarea.libc perftest to malloc-perf-autotest.
v9:
* address Dmitry's comments.
* drop features of SOURCE_USER and backup memarea mechanism.
* rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
  to keep with rte_mbuf_refcnt_update name style.
* fix memarea perftest compile failed at windows platform.
* fix spell warning.
v8:
* address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
* small feature patches are combined.
* enhanced backup memory mechanism.
* add memarea to malloc-perf-autotest.
* other tiny naming optimize.
v7:
* repost patches as there are spread over different series in patchwork.
v6:
* address Mattias's comments.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 297 +++++++++++++++++
 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 |   5 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  56 ++++
 lib/memarea/meson.build                |  10 +
 lib/memarea/rte_memarea.c              | 420 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 189 +++++++++++
 lib/memarea/version.map                |  15 +
 lib/meson.build                        |   1 +
 16 files changed, 1053 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.33.0


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v12 1/6] memarea: introduce memarea library
  2023-01-14 11:49 ` [PATCH v12 " Chengwen Feng
@ 2023-01-14 11:49   ` Chengwen Feng
  2023-01-15  7:58     ` Morten Brørup
  2023-01-14 11:49   ` [PATCH v12 2/6] test/memarea: support memarea test Chengwen Feng
                     ` (4 subsequent siblings)
  5 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2023-01-14 11:49 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk,
	hofors, liudongdong3
  Cc: thomas, dev

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>
---
 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  |  37 ++++++
 doc/guides/rel_notes/release_23_03.rst |   5 +
 lib/eal/common/eal_common_log.c        |   1 +
 lib/eal/include/rte_log.h              |   1 +
 lib/memarea/memarea_private.h          |  53 +++++++++
 lib/memarea/meson.build                |  10 ++
 lib/memarea/rte_memarea.c              | 158 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 122 +++++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 14 files changed, 409 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 9a0f416d2e..5991156227 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index de488c7abf..24456604f8 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -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),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index f0886c3bd1..8334ebcbd6 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -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 \
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..156ff35cfd
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -0,0 +1,37 @@
+..  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.
+
+Reference
+---------
+
+[1] https://en.wikipedia.org/wiki/Region-based_memory_management
diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index b8c5b68d6c..052603dffd 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -55,6 +55,11 @@ 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.
 
 Removed Items
 -------------
diff --git a/lib/eal/common/eal_common_log.c b/lib/eal/common/eal_common_log.c
index bd7b188ceb..3d62af59c6 100644
--- a/lib/eal/common/eal_common_log.c
+++ b/lib/eal/common/eal_common_log.c
@@ -369,6 +369,7 @@ static const struct logtype logtype_strings[] = {
 	{RTE_LOGTYPE_EFD,        "lib.efd"},
 	{RTE_LOGTYPE_EVENTDEV,   "lib.eventdev"},
 	{RTE_LOGTYPE_GSO,        "lib.gso"},
+	{RTE_LOGTYPE_MEMAREA,    "lib.memarea"},
 	{RTE_LOGTYPE_USER1,      "user1"},
 	{RTE_LOGTYPE_USER2,      "user2"},
 	{RTE_LOGTYPE_USER3,      "user3"},
diff --git a/lib/eal/include/rte_log.h b/lib/eal/include/rte_log.h
index 6d2b0856a5..9dc6fb593c 100644
--- a/lib/eal/include/rte_log.h
+++ b/lib/eal/include/rte_log.h
@@ -47,6 +47,7 @@ extern "C" {
 #define RTE_LOGTYPE_EFD       18 /**< Log related to EFD. */
 #define RTE_LOGTYPE_EVENTDEV  19 /**< Log related to eventdev. */
 #define RTE_LOGTYPE_GSO       20 /**< Log related to GSO. */
+#define RTE_LOGTYPE_MEMAREA   21 /**< Log related to memarea. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1     24 /**< User-defined log type 1. */
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..3d152ec780
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,53 @@
+/* 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_ALLOCATED_MAGIC	0xbeef1234
+#define MEMAREA_OBJECT_FREE_MAGIC	0xbeefabcd
+
+/*
+ * The internal layout of free memory object:
+ * ------------------------------------------------------------------------------------
+ * | object-1 |                                                        | object-2 | ...
+ * ------------------------------------------------------------------------------------
+ *          ->|                object-1's size                         |<-
+ *
+ *
+ * The internal layout of allocated memory object:
+ * ------------------------------------------------------------------------------------
+ * | object-1 | space-A | offset |   application-allocated   | space-B | object-2 | ...
+ * ------------------------------------------------------------------------------------
+ *          ->|                object-1's size                         |<-
+ *                             ->|   object-1's alloc_size   |<-
+ * The memory object's sub-region:
+ *  - space-A: space used to align the pointer which returned by alloc API.
+ *  - offset : 4bytes used to hold the offset of object-1 relative to the
+ *             pointer returned by alloc API.
+ *  - application-allocated: region applied by application invoking alloc API.
+ *  - space-B: space used to align the next object.
+ *
+ */
+struct memarea_obj {
+	TAILQ_ENTRY(memarea_obj) obj_node;
+	TAILQ_ENTRY(memarea_obj) free_node;
+	size_t                   size;
+	size_t                   alloc_size;
+	uint32_t                 magic;
+};
+
+TAILQ_HEAD(memarea_obj_list, memarea_obj);
+
+struct rte_memarea {
+	struct rte_memarea_param init;
+	rte_spinlock_t           lock;
+	void                    *area_addr;
+	struct memarea_obj_list  obj_list;
+	struct memarea_obj_list  free_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..d5c2c442f5
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2022 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..f3b3bdb09a
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,158 @@
+/* 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"
+
+static int
+memarea_check_param(const struct rte_memarea_param *init)
+{
+#define MEMAREA_MIN_SIZE	1024
+	size_t len;
+
+	if (init == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea init param is NULL!\n");
+		return -EINVAL;
+	}
+
+	len = strnlen(init->name, RTE_MEMAREA_NAMESIZE);
+	if (len == 0 || len >= RTE_MEMAREA_NAMESIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea name size %zu invalid!\n", len);
+		return -EINVAL;
+	}
+
+	if (init->source != RTE_MEMAREA_SOURCE_HEAP &&
+	    init->source != RTE_MEMAREA_SOURCE_LIBC &&
+	    init->source != RTE_MEMAREA_SOURCE_MEMAREA) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source: %d not supported!\n",
+			init->name, init->source);
+		return -EINVAL;
+	}
+
+	if (init->total_sz < MEMAREA_MIN_SIZE) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s total-size: %zu too small!\n",
+			init->name, init->total_sz);
+		return -EINVAL;
+	}
+
+	if (init->source == RTE_MEMAREA_SOURCE_MEMAREA && init->src_memarea == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s source memarea is NULL!\n", init->name);
+		return -EINVAL;
+	}
+
+	if (init->alg != RTE_MEMAREA_ALGORITHM_NEXTFIT) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s algorithm: %d not supported!\n",
+			init->name, init->alg);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+memarea_alloc_from_libc(size_t size)
+{
+#ifndef RTE_EXEC_ENV_WINDOWS
+	void *ptr = NULL;
+	int ret;
+	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+	if (ret)
+		return NULL;
+	return ptr;
+#else
+	/* Windows platform don't support posix_memalign() */
+	return malloc(size);
+#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_obj *obj;
+	struct rte_memarea *ma;
+	size_t unaligns;
+	void *addr;
+	int ret;
+
+	ret = memarea_check_param(init);
+	if (ret)
+		return NULL;
+
+	addr = memarea_alloc_area(init);
+	if (addr == NULL) {
+		RTE_LOG(ERR, MEMAREA, "memarea: %s alloc memory area fail!\n", init->name);
+		return NULL;
+	}
+
+	ma = rte_zmalloc(NULL, sizeof(struct rte_memarea), RTE_CACHE_LINE_SIZE);
+	if (ma == NULL) {
+		memarea_free_area(init, addr);
+		RTE_LOG(ERR, MEMAREA, "malloc memarea: %s management obj fail!\n", init->name);
+		return NULL;
+	}
+
+	ma->init = *init;
+	rte_spinlock_init(&ma->lock);
+	TAILQ_INIT(&ma->obj_list);
+	TAILQ_INIT(&ma->free_list);
+	ma->area_addr = addr;
+	/* The address returned by the windows platform may not be cache-line aligned.
+	 * In this case, the object address and total_sz should be adjusted.
+	 */
+	unaligns = ((uintptr_t)addr) & (RTE_CACHE_LINE_SIZE - 1);
+	if (unaligns > 0) {
+		obj = RTE_PTR_ADD(addr, RTE_CACHE_LINE_SIZE - unaligns);
+		ma->init.total_sz -= (RTE_CACHE_LINE_SIZE - unaligns);
+	} else {
+		obj = addr;
+	}
+	TAILQ_INSERT_TAIL(&ma->obj_list, obj, obj_node);
+	TAILQ_INSERT_TAIL(&ma->free_list, obj, free_node);
+	obj->size = init->total_sz - sizeof(struct memarea_obj);
+	obj->alloc_size = 0;
+	obj->magic = MEMAREA_OBJECT_FREE_MAGIC;
+
+	return ma;
+}
+
+void
+rte_memarea_destroy(struct rte_memarea *ma)
+{
+	if (ma == NULL)
+		return;
+	memarea_free_area(&ma->init, ma->area_addr);
+	rte_free(ma);
+}
+
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
new file mode 100644
index 0000000000..9983308ae8
--- /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.
+ */
+
+#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_memarea;
+	};
+};
+
+/**
+ * @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 */
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 a90fee31b7..f561875305 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.33.0


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v12 2/6] test/memarea: support memarea test
  2023-01-14 11:49 ` [PATCH v12 " Chengwen Feng
  2023-01-14 11:49   ` [PATCH v12 1/6] memarea: " Chengwen Feng
@ 2023-01-14 11:49   ` Chengwen Feng
  2023-01-14 11:49   ` [PATCH v12 3/6] memarea: support alloc and free API Chengwen Feng
                     ` (3 subsequent siblings)
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-01-14 11:49 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk,
	hofors, liudongdong3
  Cc: thomas, dev

This patch supports memarea test of rte_memarea_create() and
rte_memarea_destroy() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 130 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 5991156227..7badd4ad1a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1579,6 +1579,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index f34d19e3c3..fde0155cf9 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -84,6 +84,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -200,6 +201,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..c9de779a1d
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) { \
+			printf("%s Failed\n", #test_func); \
+			fails++; \
+		} else { \
+			printf("%s Passed\n", #test_func); \
+		} \
+	} while (0)
+
+static int fails;
+
+static void
+test_memarea_prepare(void)
+{
+	fails = 0;
+}
+
+static int
+test_memarea_retcode(void)
+{
+	return fails > 0 ? -1 : 0;
+}
+
+static void
+test_memarea_init_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "test-memarea");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for memarea NULL */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for algorithm invalid */
+	test_memarea_init_param(&init);
+	init.alg = RTE_MEMAREA_ALGORITHM_NEXTFIT + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for create with HEAP */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.numa_socket = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	test_memarea_prepare();
+
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+
+	return test_memarea_retcode();
+}
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.33.0


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v12 3/6] memarea: support alloc and free API
  2023-01-14 11:49 ` [PATCH v12 " Chengwen Feng
  2023-01-14 11:49   ` [PATCH v12 1/6] memarea: " Chengwen Feng
  2023-01-14 11:49   ` [PATCH v12 2/6] test/memarea: support memarea test Chengwen Feng
@ 2023-01-14 11:49   ` Chengwen Feng
  2023-01-14 11:49   ` [PATCH v12 4/6] test/memarea: support alloc and free API test Chengwen Feng
                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-01-14 11:49 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk,
	hofors, liudongdong3
  Cc: thomas, dev

This patch supports rte_memarea_alloc() and rte_memarea_free() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   6 ++
 lib/memarea/memarea_private.h         |   3 +
 lib/memarea/rte_memarea.c             | 146 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  46 ++++++++
 lib/memarea/version.map               |   2 +
 5 files changed, 203 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 156ff35cfd..01187f7ccb 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -31,6 +31,12 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
 Reference
 ---------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 3d152ec780..509cbc7bc7 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -48,6 +48,9 @@ struct rte_memarea {
 	void                    *area_addr;
 	struct memarea_obj_list  obj_list;
 	struct memarea_obj_list  free_list;
+
+	uint64_t                 alloc_fails;
+	uint64_t                 magic_check_fails;
 } __rte_cache_aligned;
 
 #endif /* MEMAREA_PRIVATE_H */
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index f3b3bdb09a..76f8083d96 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,8 +2,10 @@
  * Copyright(c) 2023 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -84,6 +86,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 					init->numa_socket);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		ptr = memarea_alloc_from_libc(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		ptr = rte_memarea_alloc(init->src_memarea, init->total_sz, 0);
 
 	return ptr;
 }
@@ -95,6 +99,8 @@ memarea_free_area(const struct rte_memarea_param *init, void *ptr)
 		rte_free(ptr);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ptr);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		rte_memarea_free(init->src_memarea, ptr);
 }
 
 struct rte_memarea *
@@ -156,3 +162,143 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	rte_free(ma);
 }
 
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+static inline uint32_t
+memarea_calc_align_space(struct memarea_obj *obj, uint32_t align)
+{
+	if (align == 0)
+		return 0;
+	return align - (((uintptr_t)obj + sizeof(struct memarea_obj) + sizeof(uint32_t)) &
+			(align - 1));
+}
+
+static inline bool
+memarea_whether_add_node(size_t obj_size, size_t need_size)
+{
+	return (obj_size - need_size) > sizeof(struct memarea_obj) + RTE_CACHE_LINE_SIZE;
+}
+
+static inline void
+memarea_add_node(struct rte_memarea *ma, struct memarea_obj *obj, size_t used_size)
+{
+	size_t align_size = RTE_ALIGN_CEIL(used_size, sizeof(void *));
+	struct memarea_obj *new_obj;
+
+	new_obj = (struct memarea_obj *)RTE_PTR_ADD(obj, sizeof(struct memarea_obj) +
+							 align_size);
+	new_obj->size = obj->size - align_size - sizeof(struct memarea_obj);
+	new_obj->alloc_size = 0;
+	new_obj->magic = MEMAREA_OBJECT_FREE_MAGIC;
+	TAILQ_INSERT_AFTER(&ma->obj_list, obj, new_obj, obj_node);
+	TAILQ_INSERT_AFTER(&ma->free_list, obj, new_obj, free_node);
+	obj->size = align_size;
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t align)
+{
+	size_t size_req = size + align + sizeof(uint32_t); /* use to check size overflow */
+	struct memarea_obj *obj;
+	uint32_t align_space;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0 || size_req < size ||
+		(align && !rte_is_power_of_2(align))))
+		return ptr;
+
+	memarea_lock(ma);
+	TAILQ_FOREACH(obj, &ma->free_list, free_node) {
+		if (unlikely(obj->magic != MEMAREA_OBJECT_FREE_MAGIC)) {
+			ma->magic_check_fails++;
+			RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail when alloc object!\n",
+				ma->init.name, obj->magic);
+			break;
+		}
+		align_space = memarea_calc_align_space(obj, align);
+		if (obj->size < size + align_space)
+			continue;
+		if (memarea_whether_add_node(obj->size, size + align_space))
+			memarea_add_node(ma, obj, size + align_space);
+		obj->alloc_size = size;
+		obj->magic = MEMAREA_OBJECT_ALLOCATED_MAGIC;
+		TAILQ_REMOVE(&ma->free_list, obj, free_node);
+		ptr = RTE_PTR_ADD(obj, sizeof(struct memarea_obj) + align_space + sizeof(uint32_t));
+		*(uint32_t *)RTE_PTR_SUB(ptr, sizeof(uint32_t)) = (uintptr_t)ptr - (uintptr_t)obj;
+		break;
+	}
+	if (unlikely(ptr == NULL))
+		ma->alloc_fails++;
+	memarea_unlock(ma);
+
+	return ptr;
+}
+
+static inline void
+memarea_merge_node(struct rte_memarea *ma, struct memarea_obj *curr,
+		   struct memarea_obj *next, bool del_next_from_free,
+		   bool add_curr_to_free)
+{
+	curr->size += next->size + sizeof(struct memarea_obj);
+	next->alloc_size = 0;
+	next->magic = 0;
+	TAILQ_REMOVE(&ma->obj_list, next, obj_node);
+	if (del_next_from_free)
+		TAILQ_REMOVE(&ma->free_list, next, free_node);
+	if (add_curr_to_free) {
+		curr->alloc_size = 0;
+		curr->magic = MEMAREA_OBJECT_FREE_MAGIC;
+		TAILQ_INSERT_TAIL(&ma->free_list, curr, free_node);
+	}
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	struct memarea_obj *obj, *prev, *next;
+	bool merged = false;
+	uint32_t offset;
+
+	if (unlikely(ma == NULL || ptr == NULL))
+		return;
+
+	offset = *(uint32_t *)RTE_PTR_SUB(ptr, sizeof(uint32_t));
+	obj = (struct memarea_obj *)RTE_PTR_SUB(ptr, offset);
+	if (unlikely(obj->magic != MEMAREA_OBJECT_ALLOCATED_MAGIC)) {
+		ma->magic_check_fails++;
+		RTE_LOG(ERR, MEMAREA, "memarea: %s magic: 0x%x check fail when free object!\n",
+			ma->init.name, obj->magic);
+		return;
+	}
+
+	memarea_lock(ma);
+	prev = TAILQ_PREV(obj, memarea_obj_list, obj_node);
+	next = TAILQ_NEXT(obj, obj_node);
+	if (prev != NULL && prev->magic == MEMAREA_OBJECT_FREE_MAGIC) {
+		memarea_merge_node(ma, prev, obj, false, false);
+		obj = prev;
+		merged = true;
+	}
+	if (next != NULL && next->magic == MEMAREA_OBJECT_FREE_MAGIC) {
+		memarea_merge_node(ma, obj, next, true, !merged);
+		merged = true;
+	}
+	if (!merged) {
+		obj->alloc_size = 0;
+		obj->magic = MEMAREA_OBJECT_FREE_MAGIC;
+		TAILQ_INSERT_TAIL(&ma->free_list, obj, free_node);
+	}
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 9983308ae8..1e94685719 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -115,6 +115,52 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ * @param align
+ *   If 0, the return is a pointer that is aligned for uint32_t variable.
+ *   Otherwise, the return is a pointer that is a multiple of *align*. In
+ *   this case, it must be a power of two.
+ *
+ * @return
+ *   - NULL on error. Not enough memory, or invalid arguments (ma is NULL,
+ *     size is 0, align is non-zero and not a power of two).
+ *   - Otherwise, the pointer to the allocated object.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t align);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), if it is allocated from memarea-A, it must be freed to
+ * the same memarea-A. The behaviour of rte_memarea_free() is undefined if the
+ * memarea or pointer does not match these requirements.
+ *
+ * @param ma
+ *   The pointer of memarea. If the ma is NULL, the function does nothing.
+ * @param ptr
+ *   The pointer of memory object which need be freed. If the pointer is NULL,
+ *   the function does nothing.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..effbd0b488 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,10 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
 
 	local: *;
 };
-- 
2.33.0


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v12 4/6] test/memarea: support alloc and free API test
  2023-01-14 11:49 ` [PATCH v12 " Chengwen Feng
                     ` (2 preceding siblings ...)
  2023-01-14 11:49   ` [PATCH v12 3/6] memarea: support alloc and free API Chengwen Feng
@ 2023-01-14 11:49   ` Chengwen Feng
  2023-01-14 11:49   ` [PATCH v12 5/6] memarea: support dump API Chengwen Feng
  2023-01-14 11:49   ` [PATCH v12 6/6] test/memarea: support dump API test Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-01-14 11:49 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk,
	hofors, liudongdong3
  Cc: thomas, dev

This patch supports rte_memarea_alloc() and rte_memarea_free() API
test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
---
 app/test/test_memarea.c | 135 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 134 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index c9de779a1d..efabc594b7 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -47,6 +47,12 @@ test_memarea_init_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -95,8 +101,8 @@ test_memarea_create_bad_param(void)
 static int
 test_memarea_create_destroy(void)
 {
+	struct rte_memarea *ma, *src_ma;
 	struct rte_memarea_param init;
-	struct rte_memarea *ma;
 
 	/* test for create with HEAP */
 	test_memarea_init_param(&init);
@@ -113,6 +119,130 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with another memarea */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	src_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(src_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.src_memarea = src_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(src_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE - RTE_CACHE_LINE_SIZE, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	ptr[1] = rte_memarea_alloc(ma, 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test repeat free */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1, 0);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	void *ptr[ALLOC_MAX_NUM];
+	struct rte_memarea *ma;
+	int i;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1, 0);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, NULL);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -123,6 +253,9 @@ test_memarea(void)
 
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 
 	return test_memarea_retcode();
 }
-- 
2.33.0


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v12 5/6] memarea: support dump API
  2023-01-14 11:49 ` [PATCH v12 " Chengwen Feng
                     ` (3 preceding siblings ...)
  2023-01-14 11:49   ` [PATCH v12 4/6] test/memarea: support alloc and free API test Chengwen Feng
@ 2023-01-14 11:49   ` Chengwen Feng
  2023-01-14 11:49   ` [PATCH v12 6/6] test/memarea: support dump API test Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-01-14 11:49 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk,
	hofors, liudongdong3
  Cc: thomas, dev

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   3 +
 lib/memarea/rte_memarea.c             | 116 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  21 +++++
 lib/memarea/version.map               |   1 +
 4 files changed, 141 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 01187f7ccb..2b5326e717 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -37,6 +37,9 @@ the memarea.
 The ``rte_memarea_free()`` function is used to free one memory object which
 allocated by ``rte_memarea_alloc()``.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Reference
 ---------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 76f8083d96..de473abe1d 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -302,3 +302,119 @@ rte_memarea_free(struct rte_memarea *ma, void *ptr)
 	}
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_MEMAREA)
+		return "memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_algorithm alg)
+{
+	if (alg == RTE_MEMAREA_ALGORITHM_NEXTFIT)
+		return "nextfit";
+	else
+		return "unknown";
+}
+
+static uint32_t
+memarea_obj_list_num(struct rte_memarea *ma)
+{
+	struct memarea_obj *obj;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(obj, &ma->obj_list, obj_node) {
+		if (obj->magic != MEMAREA_OBJECT_ALLOCATED_MAGIC &&
+		    obj->magic != MEMAREA_OBJECT_FREE_MAGIC)
+			break;
+		num++;
+	}
+
+	return num;
+}
+
+static uint32_t
+memarea_free_list_num(struct rte_memarea *ma)
+{
+	struct memarea_obj *obj;
+	uint32_t num = 0;
+
+	TAILQ_FOREACH(obj, &ma->free_list, free_node) {
+		if (obj->magic != MEMAREA_OBJECT_ALLOCATED_MAGIC &&
+		    obj->magic != MEMAREA_OBJECT_FREE_MAGIC)
+			break;
+		num++;
+	}
+
+	return num;
+}
+
+static size_t
+memarea_free_list_size(struct rte_memarea *ma)
+{
+	struct memarea_obj *obj;
+	size_t sz = 0;
+
+	TAILQ_FOREACH(obj, &ma->free_list, free_node) {
+		if (obj->magic != MEMAREA_OBJECT_ALLOCATED_MAGIC &&
+		    obj->magic != MEMAREA_OBJECT_FREE_MAGIC)
+			break;
+		sz += obj->size;
+	}
+
+	return sz;
+}
+
+static void
+memarea_dump_all_objects(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_obj *obj;
+
+	fprintf(f, "  objects:\n");
+	TAILQ_FOREACH(obj, &ma->obj_list, obj_node) {
+		if (obj->magic != MEMAREA_OBJECT_ALLOCATED_MAGIC &&
+		    obj->magic != MEMAREA_OBJECT_FREE_MAGIC) {
+			fprintf(f, "    magic: 0x%x check fail! stop dump!\n", obj->magic);
+			break;
+		}
+		fprintf(f, "    size: 0x%zx alloc-size: 0x%zx %s\n",
+			obj->size, obj->alloc_size,
+			obj->magic == MEMAREA_OBJECT_ALLOCATED_MAGIC ? "allocated" : "free");
+	}
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		fprintf(f, "  heap-numa-socket: %d\n", ma->init.numa_socket);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		fprintf(f, "  source-memarea: %s\n", ma->init.src_memarea->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+	fprintf(f, "  total-objects: %u\n", memarea_obj_list_num(ma));
+	fprintf(f, "  total-free-objects: %u\n", memarea_free_list_num(ma));
+	fprintf(f, "  total-free-objects-size: 0x%zx\n", memarea_free_list_size(ma));
+	fprintf(f, "  alloc-fails: %" PRIu64 "\n", ma->alloc_fails);
+	fprintf(f, "  magic_check_fails: %" PRIu64 "\n", ma->magic_check_fails);
+	if (dump_all)
+		memarea_dump_all_objects(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 1e94685719..6d5093c47a 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -161,6 +161,27 @@ void *rte_memarea_alloc(struct rte_memarea *ma, size_t size, uint32_t align);
 __rte_experimental
 void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index effbd0b488..9513d91e0b 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 
 	local: *;
-- 
2.33.0


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v12 6/6] test/memarea: support dump API test
  2023-01-14 11:49 ` [PATCH v12 " Chengwen Feng
                     ` (4 preceding siblings ...)
  2023-01-14 11:49   ` [PATCH v12 5/6] memarea: support dump API Chengwen Feng
@ 2023-01-14 11:49   ` Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-01-14 11:49 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk,
	hofors, liudongdong3
  Cc: thomas, dev

This patch supports rte_memarea_dump() API test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
---
 app/test/test_memarea.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index efabc594b7..f8c32ca9dd 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -246,6 +246,39 @@ test_memarea_alloc_free(void)
 	return 0;
 }
 
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, 1, 0);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE, 0);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -256,6 +289,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 
 	return test_memarea_retcode();
 }
-- 
2.33.0


^ permalink raw reply	[flat|nested] 222+ messages in thread

* RE: [PATCH v12 1/6] memarea: introduce memarea library
  2023-01-14 11:49   ` [PATCH v12 1/6] memarea: " Chengwen Feng
@ 2023-01-15  7:58     ` Morten Brørup
  2023-01-20  8:20       ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: Morten Brørup @ 2023-01-15  7:58 UTC (permalink / raw)
  To: Chengwen Feng, david.marchand, anatoly.burakov, dmitry.kozliuk,
	jerinjacobk, hofors, liudongdong3
  Cc: thomas, dev

> From: Chengwen Feng [mailto:fengchengwen@huawei.com]
> Sent: Saturday, 14 January 2023 12.50
> 
> 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>
> ---

[...]

> +struct memarea_obj {
> +	TAILQ_ENTRY(memarea_obj) obj_node;
> +	TAILQ_ENTRY(memarea_obj) free_node;
> +	size_t                   size;
> +	size_t                   alloc_size;
> +	uint32_t                 magic;
> +};

The magic cookie is for debug purposes only, and should be enclosed by #ifdef RTE_LIBRTE_MEMAREA_DEBUG, like in the mempool library [1].

[1]: https://elixir.bootlin.com/dpdk/latest/source/lib/mempool/rte_mempool.h#L154

With that added:

Series-acked-by: Morten Brørup <mb@smartsharesystems.com>


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v12 1/6] memarea: introduce memarea library
  2023-01-15  7:58     ` Morten Brørup
@ 2023-01-20  8:20       ` fengchengwen
  2023-01-20  9:05         ` Morten Brørup
  0 siblings, 1 reply; 222+ messages in thread
From: fengchengwen @ 2023-01-20  8:20 UTC (permalink / raw)
  To: Morten Brørup, david.marchand, anatoly.burakov,
	dmitry.kozliuk, jerinjacobk, hofors, liudongdong3
  Cc: thomas, dev

Hi Morten,

On 2023/1/15 15:58, Morten Brørup wrote:
>> From: Chengwen Feng [mailto:fengchengwen@huawei.com]
>> Sent: Saturday, 14 January 2023 12.50
>>
>> 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>
>> ---
> 
> [...]
> 
>> +struct memarea_obj {
>> +	TAILQ_ENTRY(memarea_obj) obj_node;
>> +	TAILQ_ENTRY(memarea_obj) free_node;
>> +	size_t                   size;
>> +	size_t                   alloc_size;
>> +	uint32_t                 magic;
>> +};
> 
> The magic cookie is for debug purposes only, and should be enclosed by #ifdef RTE_LIBRTE_MEMAREA_DEBUG, like in the mempool library [1].

In the mempool the cookie mechanism is under debug macro, the main reason should be performance considerations.

And community recommends that compilation macro be used as little as possible.

So I think we could add the following new API like:

/*
 * Enable audit in alloc/free/audit.
 */
rte_memarea_audit_enable(struct rte_memarea *ma)

/*
 * Disable audit in alloc/free/audit.
 */
rte_memarea_audit_disable(struct rte_memarea *ma)

/*
 * if ptr is NULL, then audit the all objects.
 * else, only audit the object which the ptr pointers.
 * if audit fail, will return an error code, and the error log will emit.
 *
 * The audit is performed only when the audit function is enabled for the memarea.
 */
int rte_memarea_audit_object(struct rte_memarea *ma, void *ptr)


So for an application, it can invoke rte_memarea_audit() in its code without macro control.
If it considers that memory corruption may occur in a certain scenario, the audit function can be enabled, so could find it by error log or retcode.

This method causes slight performance loss. But it's guaranteed that there's no need to recompile, and a binary can be done.

> 
> [1]: https://elixir.bootlin.com/dpdk/latest/source/lib/mempool/rte_mempool.h#L154
> 
> With that added:
> 
> Series-acked-by: Morten Brørup <mb@smartsharesystems.com>
> 
> 
> .
> 

^ permalink raw reply	[flat|nested] 222+ messages in thread

* RE: [PATCH v12 1/6] memarea: introduce memarea library
  2023-01-20  8:20       ` fengchengwen
@ 2023-01-20  9:05         ` Morten Brørup
  0 siblings, 0 replies; 222+ messages in thread
From: Morten Brørup @ 2023-01-20  9:05 UTC (permalink / raw)
  To: fengchengwen, david.marchand, anatoly.burakov, dmitry.kozliuk,
	jerinjacobk, hofors, liudongdong3
  Cc: thomas, dev

> From: fengchengwen [mailto:fengchengwen@huawei.com]
> Sent: Friday, 20 January 2023 09.21
> 
> Hi Morten,
> 
> On 2023/1/15 15:58, Morten Brørup wrote:
> >> From: Chengwen Feng [mailto:fengchengwen@huawei.com]
> >> Sent: Saturday, 14 January 2023 12.50
> >>
> >> 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>
> >> ---
> >
> > [...]
> >
> >> +struct memarea_obj {
> >> +	TAILQ_ENTRY(memarea_obj) obj_node;
> >> +	TAILQ_ENTRY(memarea_obj) free_node;
> >> +	size_t                   size;
> >> +	size_t                   alloc_size;
> >> +	uint32_t                 magic;
> >> +};
> >
> > The magic cookie is for debug purposes only, and should be enclosed
> by #ifdef RTE_LIBRTE_MEMAREA_DEBUG, like in the mempool library [1].

It was just one example; the mbuf library also has RTE_LIBRTE_MBUF_DEBUG.

> 
> In the mempool the cookie mechanism is under debug macro, the main
> reason should be performance considerations.

Yes, the main reason is performance (of production builds).

The secondary reason is pollution of the generated code - unnecessary bloat makes it harder reading the assembly output when debugging, because the assembly output is not clean, but full of irrelevant runtime checks.

> 
> And community recommends that compilation macro be used as little as
> possible.

Yes, but I don't think this recommendation applies to debug code.

I strongly oppose to having run-time bug detection code, such as checking magic cookies, in production builds. It is a well established "best practice" to be able to build software projects for debug or production, and the DPDK project should not deviate from this best practice!

> 
> So I think we could add the following new API like:
> 
> /*
>  * Enable audit in alloc/free/audit.
>  */
> rte_memarea_audit_enable(struct rte_memarea *ma)
> 
> /*
>  * Disable audit in alloc/free/audit.
>  */
> rte_memarea_audit_disable(struct rte_memarea *ma)
> 
> /*
>  * if ptr is NULL, then audit the all objects.
>  * else, only audit the object which the ptr pointers.
>  * if audit fail, will return an error code, and the error log will
> emit.
>  *
>  * The audit is performed only when the audit function is enabled for
> the memarea.
>  */
> int rte_memarea_audit_object(struct rte_memarea *ma, void *ptr)
> 
> 
> So for an application, it can invoke rte_memarea_audit() in its code
> without macro control.
> If it considers that memory corruption may occur in a certain scenario,
> the audit function can be enabled, so could find it by error log or
> retcode.
> 
> This method causes slight performance loss. But it's guaranteed that
> there's no need to recompile, and a binary can be done.

It still pollutes the generated code, making debugging by hand (i.e. reading assembly output) more difficult.

> 
> >
> > [1]:
> https://elixir.bootlin.com/dpdk/latest/source/lib/mempool/rte_mempool.h
> #L154
> >
> > With that added:
> >
> > Series-acked-by: Morten Brørup <mb@smartsharesystems.com>

If you think that this library's cookie checking is important for typical users, you can leave RTE_LIBRTE_MEMAREA_DEBUG enabled by default.

But it must be possible to completely omit such run-time debug checks when building for production.


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v13 0/6] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (16 preceding siblings ...)
  2023-01-14 11:49 ` [PATCH v12 " Chengwen Feng
@ 2023-02-08  8:24 ` Chengwen Feng
  2023-02-08  8:24   ` [PATCH v13 1/6] memarea: " Chengwen Feng
                     ` (6 more replies)
  2023-02-09  6:36 ` [PATCH v14 " Chengwen Feng
                   ` (5 subsequent siblings)
  23 siblings, 7 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-02-08  8:24 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- 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 from another memarea.

- It supports MT-safe as long as it's specified at creation time.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Chengwen Feng (6):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc and free API
  test/memarea: support alloc and free API test
  memarea: support dump API
  test/memarea: support dump API test

---
v13:
* address Morten's comments.
* reduce management data overhead.
v12:
* remove rte_memarea_refcnt_update() API which address Dongdong's
  comments.
* refine the variable naming.
* fix some bugs.
v11:
* rebase 23.03
* remove "app/test: add memarea to malloc-perf-autotest" because the
  two algorithm are not comparable which also address previous
  comments.
v10:
* support windows platform.
* add rte_memarea.libc perftest to malloc-perf-autotest.
v9:
* address Dmitry's comments.
* drop features of SOURCE_USER and backup memarea mechanism.
* rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
  to keep with rte_mbuf_refcnt_update name style.
* fix memarea perftest compile failed at windows platform.
* fix spell warning.
v8:
* address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
* small feature patches are combined.
* enhanced backup memory mechanism.
* add memarea to malloc-perf-autotest.
* other tiny naming optimize.
v7:
* repost patches as there are spread over different series in patchwork.
v6:
* address Mattias's comments.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 297 ++++++++++++++++
 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  |  55 +++
 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              | 448 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 185 ++++++++++
 lib/memarea/version.map                |  15 +
 lib/meson.build                        |   1 +
 14 files changed, 1087 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v13 1/6] memarea: introduce memarea library
  2023-02-08  8:24 ` [PATCH v13 0/6] introduce memarea library Chengwen Feng
@ 2023-02-08  8:24   ` Chengwen Feng
  2023-02-09  0:04     ` Stephen Hemminger
  2023-02-08  8:24   ` [PATCH v13 2/6] test/memarea: support memarea test Chengwen Feng
                     ` (5 subsequent siblings)
  6 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2023-02-08  8:24 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

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              | 171 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 122 ++++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 12 files changed, 435 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 3495946d0f..60078ffc72 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index de488c7abf..24456604f8 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -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),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index f0886c3bd1..8334ebcbd6 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -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 \
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..3630328642
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -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
diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index 1fa101c420..afc7cd3530 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -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.
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..c41d071032
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -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 */
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..523f34eb41
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,171 @@
+/* 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, ...) \
+	rte_log(RTE_LOG_ ## level, rte_memarea_logtype, RTE_FMT("memarea: " \
+		RTE_FMT_HEAD(__VA_ARGS__,) "\n", RTE_FMT_TAIL(__VA_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, "malloc %s 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);
+}
+
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.
+ */
+
+#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 */
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 a90fee31b7..f561875305 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v13 2/6] test/memarea: support memarea test
  2023-02-08  8:24 ` [PATCH v13 0/6] introduce memarea library Chengwen Feng
  2023-02-08  8:24   ` [PATCH v13 1/6] memarea: " Chengwen Feng
@ 2023-02-08  8:24   ` Chengwen Feng
  2023-02-08  8:24   ` [PATCH v13 3/6] memarea: support alloc and free API Chengwen Feng
                     ` (4 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-02-08  8:24 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports memarea test of 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             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 130 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 60078ffc72..4bd47fb478 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1579,6 +1579,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index f34d19e3c3..fde0155cf9 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -84,6 +84,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -200,6 +201,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..db5efc4c1b
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) { \
+			printf("%s Failed\n", #test_func); \
+			fails++; \
+		} else { \
+			printf("%s Passed\n", #test_func); \
+		} \
+	} while (0)
+
+static int fails;
+
+static void
+test_memarea_prepare(void)
+{
+	fails = 0;
+}
+
+static int
+test_memarea_retcode(void)
+{
+	return fails > 0 ? -1 : 0;
+}
+
+static void
+test_memarea_init_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "autotest");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for memarea NULL */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for algorithm invalid */
+	test_memarea_init_param(&init);
+	init.alg = RTE_MEMAREA_ALGORITHM_NEXTFIT + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for create with HEAP */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.numa_socket = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	test_memarea_prepare();
+
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+
+	return test_memarea_retcode();
+}
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v13 3/6] memarea: support alloc and free API
  2023-02-08  8:24 ` [PATCH v13 0/6] introduce memarea library Chengwen Feng
  2023-02-08  8:24   ` [PATCH v13 1/6] memarea: " Chengwen Feng
  2023-02-08  8:24   ` [PATCH v13 2/6] test/memarea: support memarea test Chengwen Feng
@ 2023-02-08  8:24   ` Chengwen Feng
  2023-02-08  8:24   ` [PATCH v13 4/6] test/memarea: support alloc and free API test Chengwen Feng
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-02-08  8:24 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_alloc() and rte_memarea_free() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   6 +
 lib/memarea/rte_memarea.c             | 180 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  42 ++++++
 lib/memarea/version.map               |   2 +
 4 files changed, 230 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 3630328642..55ca4b1166 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -31,6 +31,12 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 523f34eb41..158e24b7f3 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,8 +2,10 @@
  * Copyright(c) 2023 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -88,6 +90,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 					init->numa_socket);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		ptr = memarea_alloc_from_libc(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		ptr = rte_memarea_alloc(init->src_ma, init->total_sz);
 
 	return ptr;
 }
@@ -99,6 +103,8 @@ memarea_free_area(const struct rte_memarea_param *init, void *ptr)
 		rte_free(ptr);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ptr);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		rte_memarea_free(init->src_ma, ptr);
 }
 
 struct rte_memarea *
@@ -169,3 +175,177 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	rte_free(ma);
 }
 
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+/**
+ * Check cookie or panic.
+ *
+ * @param status
+ *   - 0: object is supposed to be available
+ *   - 1: object is supposed to be allocated
+ *   - 2: just check that cookie is valid (available or allocated)
+ */
+static inline void
+memarea_check_cookie(const struct rte_memarea *ma, const struct memarea_objhdr *hdr, int status)
+{
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	static const char *const str[] = { " PASS", " FAILED" };
+	struct memarea_objtlr *tlr;
+	bool hdr_fail, tlr_fail;
+
+	if (unlikely(hdr == ma->guard_hdr))
+		return;
+
+	tlr = RTE_PTR_SUB(TAILQ_NEXT(hdr, obj_next), sizeof(struct memarea_objtlr));
+	hdr_fail = (status == 0 && hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE) ||
+		   (status == 1 && hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE) ||
+		   (status == 2 && (hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE &&
+				    hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE));
+	tlr_fail = (tlr->cookie != MEMAREA_OBJECT_TRAILER_COOKIE);
+	if (!hdr_fail && !tlr_fail)
+		return;
+
+	rte_panic("MEMAREA: %s check cookies failed! addr-%p header-cookie<0x%" PRIx64 "%s> trailer-cookie<0x%" PRIx64 "%s>\n",
+		ma->init.name, RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr)),
+		hdr->cookie, str[hdr_fail], tlr->cookie, str[tlr_fail]);
+#else
+	RTE_SET_USED(ma);
+	RTE_SET_USED(hdr);
+	RTE_SET_USED(status);
+#endif
+}
+
+static inline bool
+memarea_whether_add_node(size_t avail_sz, size_t alloc_sz)
+{
+	return (avail_sz - alloc_sz) > (sizeof(struct memarea_objhdr) +
+					MEMAREA_OBJECT_SIZE_ALIGN
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+					+ sizeof(struct memarea_objtlr)
+
+#endif
+					);
+}
+
+static inline void
+memarea_add_node(struct rte_memarea *ma, struct memarea_objhdr *hdr, size_t alloc_sz)
+{
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	struct memarea_objtlr *cur_tlr;
+#endif
+	struct memarea_objhdr *new_hdr;
+
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	cur_tlr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz);
+	cur_tlr->cookie = MEMAREA_OBJECT_TRAILER_COOKIE;
+	new_hdr = RTE_PTR_ADD(cur_tlr, sizeof(struct memarea_objtlr));
+	new_hdr->cookie = MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE;
+#else
+	new_hdr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz);
+#endif
+	TAILQ_INSERT_AFTER(&ma->obj_list, hdr, new_hdr, obj_next);
+	TAILQ_INSERT_AFTER(&ma->avail_list, hdr, new_hdr, avail_next);
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size)
+{
+	size_t align_sz = RTE_ALIGN(size, MEMAREA_OBJECT_SIZE_ALIGN);
+	struct memarea_objhdr *hdr;
+	size_t avail_sz;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0 || align_sz < size))
+		return ptr;
+
+	memarea_lock(ma);
+	TAILQ_FOREACH(hdr, &ma->avail_list, avail_next) {
+		memarea_check_cookie(ma, hdr, 0);
+		avail_sz = MEMAREA_OBJECT_GET_SIZE(hdr);
+		if (avail_sz < align_sz)
+			continue;
+		if (memarea_whether_add_node(avail_sz, align_sz))
+			memarea_add_node(ma, hdr, align_sz);
+		TAILQ_REMOVE(&ma->avail_list, hdr, avail_next);
+		MEMAREA_OBJECT_MARK_ALLOCATED(hdr);
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+		hdr->cookie = MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE;
+#endif
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		break;
+	}
+	memarea_unlock(ma);
+
+	return ptr;
+}
+
+static inline void
+memarea_merge_node(struct rte_memarea *ma, struct memarea_objhdr *curr,
+		   struct memarea_objhdr *next)
+{
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	struct memarea_objtlr *tlr;
+#endif
+	RTE_SET_USED(curr);
+	TAILQ_REMOVE(&ma->obj_list, next, obj_next);
+	TAILQ_REMOVE(&ma->avail_list, next, avail_next);
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	next->cookie = 0;
+	tlr = RTE_PTR_SUB(next, sizeof(struct memarea_objtlr));
+	tlr->cookie = 0;
+#endif
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	struct memarea_objhdr *hdr, *prev, *next;
+
+	if (unlikely(ma == NULL || ptr == NULL))
+		return;
+
+	hdr = RTE_PTR_SUB(ptr, sizeof(struct memarea_objhdr));
+	if (unlikely(!MEMAREA_OBJECT_IS_ALLOCATED(hdr))) {
+		RTE_MEMAREA_LOG(ERR, "detect invalid object when free!");
+		return;
+	}
+	memarea_check_cookie(ma, hdr, 1);
+
+	memarea_lock(ma);
+
+	/** 1st: add to avail list. */
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	hdr->cookie = MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE;
+#endif
+	TAILQ_INSERT_HEAD(&ma->avail_list, hdr, avail_next);
+
+	/** 2nd: merge if previous object is avail. */
+	prev = TAILQ_PREV(hdr, memarea_objhdr_list, obj_next);
+	if (prev != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(prev)) {
+		memarea_check_cookie(ma, prev, 0);
+		memarea_merge_node(ma, prev, hdr);
+		hdr = prev;
+	}
+
+	/** 3rd: merge if next object is avail. */
+	next = TAILQ_NEXT(hdr, obj_next);
+	if (next != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(next)) {
+		memarea_check_cookie(ma, next, 0);
+		memarea_merge_node(ma, hdr, next);
+	}
+
+	memarea_unlock(ma);
+}
+
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 435dca293f..02b4825461 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -115,6 +115,48 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ *
+ * @return
+ *   - NULL on error. Not enough memory, or invalid arguments (ma is NULL,
+ *     size is 0).
+ *   - Otherwise, the pointer to the allocated object.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), it must be freed to the same memarea which previous
+ * allocated from. The behaviour of rte_memarea_free() is undefined if the
+ * memarea or pointer does not match these requirements.
+ *
+ * @param ma
+ *   The pointer of memarea. If the ma is NULL, the function does nothing.
+ * @param ptr
+ *   The pointer of memory object which need be freed. If the pointer is NULL,
+ *   the function does nothing.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..effbd0b488 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,10 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v13 4/6] test/memarea: support alloc and free API test
  2023-02-08  8:24 ` [PATCH v13 0/6] introduce memarea library Chengwen Feng
                     ` (2 preceding siblings ...)
  2023-02-08  8:24   ` [PATCH v13 3/6] memarea: support alloc and free API Chengwen Feng
@ 2023-02-08  8:24   ` Chengwen Feng
  2023-02-08  8:24   ` [PATCH v13 5/6] memarea: support dump API Chengwen Feng
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-02-08  8:24 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_alloc() and rte_memarea_free() API
test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 app/test/test_memarea.c | 135 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 134 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index db5efc4c1b..3e67e59227 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -47,6 +47,12 @@ test_memarea_init_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -95,8 +101,8 @@ test_memarea_create_bad_param(void)
 static int
 test_memarea_create_destroy(void)
 {
+	struct rte_memarea *ma, *src_ma;
 	struct rte_memarea_param init;
-	struct rte_memarea *ma;
 
 	/* test for create with HEAP */
 	test_memarea_init_param(&init);
@@ -113,6 +119,130 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with another memarea */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	src_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(src_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.src_ma = src_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(src_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test repeat free */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	void *ptr[ALLOC_MAX_NUM];
+	struct rte_memarea *ma;
+	int i;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, NULL);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -123,6 +253,9 @@ test_memarea(void)
 
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v13 5/6] memarea: support dump API
  2023-02-08  8:24 ` [PATCH v13 0/6] introduce memarea library Chengwen Feng
                     ` (3 preceding siblings ...)
  2023-02-08  8:24   ` [PATCH v13 4/6] test/memarea: support alloc and free API test Chengwen Feng
@ 2023-02-08  8:24   ` Chengwen Feng
  2023-02-08  8:24   ` [PATCH v13 6/6] test/memarea: support dump API test Chengwen Feng
  2023-02-08  8:43   ` [PATCH v13 0/6] introduce memarea library Morten Brørup
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-02-08  8:24 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +
 lib/memarea/rte_memarea.c             | 97 +++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 21 ++++++
 lib/memarea/version.map               |  1 +
 4 files changed, 122 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 55ca4b1166..d9fac69ed3 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -37,6 +37,9 @@ the memarea.
 The ``rte_memarea_free()`` function is used to free one memory object which
 allocated by ``rte_memarea_alloc()``.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 158e24b7f3..68e6e4687d 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -349,3 +349,100 @@ rte_memarea_free(struct rte_memarea *ma, void *ptr)
 	memarea_unlock(ma);
 }
 
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_MEMAREA)
+		return "memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_algorithm alg)
+{
+	if (alg == RTE_MEMAREA_ALGORITHM_NEXTFIT)
+		return "nextfit";
+	else
+		return "unknown";
+}
+
+static void
+memarea_dump_objects_brief(struct rte_memarea *ma, FILE *f)
+{
+	uint32_t total_objs = 0, total_avail_objs = 0;
+	struct memarea_objhdr *hdr;
+	size_t total_avail_sz = 0;
+
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, 2);
+		total_objs++;
+		if (!MEMAREA_OBJECT_IS_ALLOCATED(hdr)) {
+			total_avail_objs++;
+			total_avail_sz += MEMAREA_OBJECT_GET_SIZE(hdr);
+		}
+	}
+	fprintf(f, "  total-objects: %u\n", total_objs);
+	fprintf(f, "  total-avail-objects: %u\n", total_avail_objs);
+	fprintf(f, "  total-avail-objects-size: 0x%zx\n", total_avail_sz);
+}
+
+static void
+memarea_dump_objects_detail(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_objhdr *hdr;
+	size_t offset;
+	void *ptr;
+
+	fprintf(f, "  objects:\n");
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, 2);
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		offset = RTE_PTR_DIFF(ptr, ma->area_base);
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+		fprintf(f, "    %p off: 0x%zx size: 0x%zx %s\n",
+			ptr, offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#else
+		fprintf(f, "    off: 0x%zx size: 0x%zx %s\n",
+			offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#endif
+	}
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		fprintf(f, "  heap-numa-socket: %d\n", ma->init.numa_socket);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		fprintf(f, "  source-memarea: %s\n", ma->init.src_ma->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	fprintf(f, "  area-base: %p\n", ma->area_base);
+	fprintf(f, "  guard-header: %p\n", ma->guard_hdr);
+#endif
+	memarea_dump_objects_brief(ma, f);
+	if (dump_all)
+		memarea_dump_objects_detail(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 02b4825461..c871accda6 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -157,6 +157,27 @@ void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
 __rte_experimental
 void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index effbd0b488..9513d91e0b 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 
 	local: *;
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v13 6/6] test/memarea: support dump API test
  2023-02-08  8:24 ` [PATCH v13 0/6] introduce memarea library Chengwen Feng
                     ` (4 preceding siblings ...)
  2023-02-08  8:24   ` [PATCH v13 5/6] memarea: support dump API Chengwen Feng
@ 2023-02-08  8:24   ` Chengwen Feng
  2023-02-08  8:43   ` [PATCH v13 0/6] introduce memarea library Morten Brørup
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-02-08  8:24 UTC (permalink / raw)
  To: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors
  Cc: thomas, dev

This patch supports rte_memarea_dump() API test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 app/test/test_memarea.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 3e67e59227..ad40dbf613 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -246,6 +246,39 @@ test_memarea_alloc_free(void)
 	return 0;
 }
 
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -256,6 +289,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* RE: [PATCH v13 0/6] introduce memarea library
  2023-02-08  8:24 ` [PATCH v13 0/6] introduce memarea library Chengwen Feng
                     ` (5 preceding siblings ...)
  2023-02-08  8:24   ` [PATCH v13 6/6] test/memarea: support dump API test Chengwen Feng
@ 2023-02-08  8:43   ` Morten Brørup
  6 siblings, 0 replies; 222+ messages in thread
From: Morten Brørup @ 2023-02-08  8:43 UTC (permalink / raw)
  To: Chengwen Feng, david.marchand, anatoly.burakov, dmitry.kozliuk,
	jerinjacobk, hofors
  Cc: thomas, dev

> From: Chengwen Feng [mailto:fengchengwen@huawei.com]
> Sent: Wednesday, 8 February 2023 09.25
> 
> The memarea library is an allocator of variable-size object which based
> on a memory region. The main features are as follows:
> 
> - 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 from another memarea.
> 
> - It supports MT-safe as long as it's specified at creation time.
> 
> Note:
> a) The memarea is oriented towards the application layer, which could
> provides 'region-based memory management' [1] function.
> b) The eal library also provide memory zone/heap management, but these
> are tied to huge pages management.
> 
> [1] https://en.wikipedia.org/wiki/Region-based_memory_management
> 
> Chengwen Feng (6):
>   memarea: introduce memarea library
>   test/memarea: support memarea test
>   memarea: support alloc and free API
>   test/memarea: support alloc and free API test
>   memarea: support dump API
>   test/memarea: support dump API test
> 
> ---
> v13:
> * address Morten's comments.

This means: "Make debug cookies optional, controlled by RTE_LIBRTE_MEMAREA_DEBUG; disabled by default."

Thank you.


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v13 1/6] memarea: introduce memarea library
  2023-02-08  8:24   ` [PATCH v13 1/6] memarea: " Chengwen Feng
@ 2023-02-09  0:04     ` Stephen Hemminger
  2023-02-09  6:48       ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: Stephen Hemminger @ 2023-02-09  0:04 UTC (permalink / raw)
  To: Chengwen Feng
  Cc: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk,
	hofors, thomas, dev

On Wed, 8 Feb 2023 08:24:47 +0000
Chengwen Feng <fengchengwen@huawei.com> wrote:

> +RTE_LOG_REGISTER_DEFAULT(rte_memarea_logtype, INFO);
> +#define RTE_MEMAREA_LOG(level, ...) \
> +	rte_log(RTE_LOG_ ## level, rte_memarea_logtype, RTE_FMT("memarea: " \
> +		RTE_FMT_HEAD(__VA_ARGS__,) "\n", RTE_FMT_TAIL(__VA_ARGS__,)))
> +

For me it is easier to read as:
#define RTE_MEMAREA_LOG(level, fmt, args...) \
	rte_log(RTE_LOG_ ## level, rte_memarea_logtype, \
		"memarea: " fmt "\n", ## args)


A common convention is to use "%s: " and __func__.

^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v14 0/6] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (17 preceding siblings ...)
  2023-02-08  8:24 ` [PATCH v13 0/6] introduce memarea library Chengwen Feng
@ 2023-02-09  6:36 ` Chengwen Feng
  2023-02-09  6:36   ` [PATCH v14 1/6] memarea: " Chengwen Feng
                     ` (6 more replies)
  2023-07-09 13:00 ` [PATCH v15 " Chengwen Feng
                   ` (4 subsequent siblings)
  23 siblings, 7 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-02-09  6:36 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- 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 from another memarea.

- It supports MT-safe as long as it's specified at creation time.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Chengwen Feng (6):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc and free API
  test/memarea: support alloc and free API test
  memarea: support dump API
  test/memarea: support dump API test

---
v14:
* address Stephen's comments: RTE_MEMAREA_LOG use easy impl and add
  __func__ print.
v13:
* address Morten's comments: make debug cookies optional, controlled
  by RTE_LIBRTE_MEMAREA_DEBUG; disabled by default.
* reduce management data overhead.
v12:
* remove rte_memarea_refcnt_update() API which address Dongdong's
  comments.
* refine the variable naming.
* fix some bugs.
v11:
* rebase 23.03
* remove "app/test: add memarea to malloc-perf-autotest" because the
  two algorithm are not comparable which also address previous
  comments.
v10:
* support windows platform.
* add rte_memarea.libc perftest to malloc-perf-autotest.
v9:
* address Dmitry's comments.
* drop features of SOURCE_USER and backup memarea mechanism.
* rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
  to keep with rte_mbuf_refcnt_update name style.
* fix memarea perftest compile failed at windows platform.
* fix spell warning.
v8:
* address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
* small feature patches are combined.
* enhanced backup memory mechanism.
* add memarea to malloc-perf-autotest.
* other tiny naming optimize.
v7:
* repost patches as there are spread over different series in patchwork.
v6:
* address Mattias's comments.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 297 ++++++++++++++++
 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  |  55 +++
 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              | 448 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 185 ++++++++++
 lib/memarea/version.map                |  15 +
 lib/meson.build                        |   1 +
 14 files changed, 1087 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v14 1/6] memarea: introduce memarea library
  2023-02-09  6:36 ` [PATCH v14 " Chengwen Feng
@ 2023-02-09  6:36   ` Chengwen Feng
  2023-06-21 10:52     ` Burakov, Anatoly
  2023-06-21 12:05     ` Burakov, Anatoly
  2023-02-09  6:36   ` [PATCH v14 2/6] test/memarea: support memarea test Chengwen Feng
                     ` (5 subsequent siblings)
  6 siblings, 2 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-02-09  6:36 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

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

diff --git a/MAINTAINERS b/MAINTAINERS
index 3495946d0f..60078ffc72 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index de488c7abf..24456604f8 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -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),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index f0886c3bd1..8334ebcbd6 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -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 \
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index 8564883018..e9015d65e3 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -37,6 +37,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..3630328642
--- /dev/null
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -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
diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index 1fa101c420..afc7cd3530 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -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.
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
new file mode 100644
index 0000000000..c41d071032
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -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 */
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..63045aa5d8
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -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);
+}
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.
+ */
+
+#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 */
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 a90fee31b7..f561875305 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -42,6 +42,7 @@ libraries = [
         'kni',
         'latencystats',
         'lpm',
+        'memarea',
         'member',
         'pcapng',
         'power',
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v14 2/6] test/memarea: support memarea test
  2023-02-09  6:36 ` [PATCH v14 " Chengwen Feng
  2023-02-09  6:36   ` [PATCH v14 1/6] memarea: " Chengwen Feng
@ 2023-02-09  6:36   ` Chengwen Feng
  2023-06-21 11:00     ` Burakov, Anatoly
  2023-02-09  6:36   ` [PATCH v14 3/6] memarea: support alloc and free API Chengwen Feng
                     ` (4 subsequent siblings)
  6 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2023-02-09  6:36 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports memarea test of 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             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 130 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 60078ffc72..4bd47fb478 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1579,6 +1579,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index f34d19e3c3..fde0155cf9 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -84,6 +84,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -200,6 +201,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..db5efc4c1b
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "test.h"
+
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+#define MEMAREA_TEST_API_RUN(test_func) \
+	do { \
+		int ret = test_func(); \
+		if (ret < 0) { \
+			printf("%s Failed\n", #test_func); \
+			fails++; \
+		} else { \
+			printf("%s Passed\n", #test_func); \
+		} \
+	} while (0)
+
+static int fails;
+
+static void
+test_memarea_prepare(void)
+{
+	fails = 0;
+}
+
+static int
+test_memarea_retcode(void)
+{
+	return fails > 0 ? -1 : 0;
+}
+
+static void
+test_memarea_init_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "autotest");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for invalid source */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for total_sz */
+	test_memarea_init_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for memarea NULL */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	/* test for algorithm invalid */
+	test_memarea_init_param(&init);
+	init.alg = RTE_MEMAREA_ALGORITHM_NEXTFIT + 1;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma == NULL, "Expected NULL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for create with HEAP */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.numa_socket = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea(void)
+{
+	test_memarea_prepare();
+
+	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
+	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+
+	return test_memarea_retcode();
+}
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v14 3/6] memarea: support alloc and free API
  2023-02-09  6:36 ` [PATCH v14 " Chengwen Feng
  2023-02-09  6:36   ` [PATCH v14 1/6] memarea: " Chengwen Feng
  2023-02-09  6:36   ` [PATCH v14 2/6] test/memarea: support memarea test Chengwen Feng
@ 2023-02-09  6:36   ` Chengwen Feng
  2023-06-22 15:29     ` Burakov, Anatoly
  2023-02-09  6:36   ` [PATCH v14 4/6] test/memarea: support alloc and free API test Chengwen Feng
                     ` (3 subsequent siblings)
  6 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2023-02-09  6:36 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_alloc() and rte_memarea_free() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   6 +
 lib/memarea/rte_memarea.c             | 180 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  42 ++++++
 lib/memarea/version.map               |   2 +
 4 files changed, 230 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 3630328642..55ca4b1166 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -31,6 +31,12 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 63045aa5d8..0b53bfe757 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,8 +2,10 @@
  * Copyright(c) 2023 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_log.h>
@@ -88,6 +90,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 					init->numa_socket);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		ptr = memarea_alloc_from_libc(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		ptr = rte_memarea_alloc(init->src_ma, init->total_sz);
 
 	return ptr;
 }
@@ -99,6 +103,8 @@ memarea_free_area(const struct rte_memarea_param *init, void *ptr)
 		rte_free(ptr);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ptr);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		rte_memarea_free(init->src_ma, ptr);
 }
 
 struct rte_memarea *
@@ -168,3 +174,177 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(&ma->init, ma->area_base);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+/**
+ * Check cookie or panic.
+ *
+ * @param status
+ *   - 0: object is supposed to be available
+ *   - 1: object is supposed to be allocated
+ *   - 2: just check that cookie is valid (available or allocated)
+ */
+static inline void
+memarea_check_cookie(const struct rte_memarea *ma, const struct memarea_objhdr *hdr, int status)
+{
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	static const char *const str[] = { "PASS", "FAILED" };
+	struct memarea_objtlr *tlr;
+	bool hdr_fail, tlr_fail;
+
+	if (unlikely(hdr == ma->guard_hdr))
+		return;
+
+	tlr = RTE_PTR_SUB(TAILQ_NEXT(hdr, obj_next), sizeof(struct memarea_objtlr));
+	hdr_fail = (status == 0 && hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE) ||
+		   (status == 1 && hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE) ||
+		   (status == 2 && (hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE &&
+				    hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE));
+	tlr_fail = (tlr->cookie != MEMAREA_OBJECT_TRAILER_COOKIE);
+	if (!hdr_fail && !tlr_fail)
+		return;
+
+	rte_panic("MEMAREA: %s check cookies failed! addr-%p header-cookie<0x%" PRIx64 " %s> trailer-cookie<0x%" PRIx64 " %s>\n",
+		ma->init.name, RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr)),
+		hdr->cookie, str[hdr_fail], tlr->cookie, str[tlr_fail]);
+#else
+	RTE_SET_USED(ma);
+	RTE_SET_USED(hdr);
+	RTE_SET_USED(status);
+#endif
+}
+
+static inline bool
+memarea_whether_add_node(size_t avail_sz, size_t alloc_sz)
+{
+	return (avail_sz - alloc_sz) > (sizeof(struct memarea_objhdr) +
+					MEMAREA_OBJECT_SIZE_ALIGN
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+					+ sizeof(struct memarea_objtlr)
+
+#endif
+					);
+}
+
+static inline void
+memarea_add_node(struct rte_memarea *ma, struct memarea_objhdr *hdr, size_t alloc_sz)
+{
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	struct memarea_objtlr *cur_tlr;
+#endif
+	struct memarea_objhdr *new_hdr;
+
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	cur_tlr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz);
+	cur_tlr->cookie = MEMAREA_OBJECT_TRAILER_COOKIE;
+	new_hdr = RTE_PTR_ADD(cur_tlr, sizeof(struct memarea_objtlr));
+	new_hdr->cookie = MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE;
+#else
+	new_hdr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz);
+#endif
+	TAILQ_INSERT_AFTER(&ma->obj_list, hdr, new_hdr, obj_next);
+	TAILQ_INSERT_AFTER(&ma->avail_list, hdr, new_hdr, avail_next);
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size)
+{
+	size_t align_sz = RTE_ALIGN(size, MEMAREA_OBJECT_SIZE_ALIGN);
+	struct memarea_objhdr *hdr;
+	size_t avail_sz;
+	void *ptr = NULL;
+
+	if (unlikely(ma == NULL || size == 0 || align_sz < size))
+		return ptr;
+
+	memarea_lock(ma);
+	TAILQ_FOREACH(hdr, &ma->avail_list, avail_next) {
+		memarea_check_cookie(ma, hdr, 0);
+		avail_sz = MEMAREA_OBJECT_GET_SIZE(hdr);
+		if (avail_sz < align_sz)
+			continue;
+		if (memarea_whether_add_node(avail_sz, align_sz))
+			memarea_add_node(ma, hdr, align_sz);
+		TAILQ_REMOVE(&ma->avail_list, hdr, avail_next);
+		MEMAREA_OBJECT_MARK_ALLOCATED(hdr);
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+		hdr->cookie = MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE;
+#endif
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		break;
+	}
+	memarea_unlock(ma);
+
+	return ptr;
+}
+
+static inline void
+memarea_merge_node(struct rte_memarea *ma, struct memarea_objhdr *curr,
+		   struct memarea_objhdr *next)
+{
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	struct memarea_objtlr *tlr;
+#endif
+	RTE_SET_USED(curr);
+	TAILQ_REMOVE(&ma->obj_list, next, obj_next);
+	TAILQ_REMOVE(&ma->avail_list, next, avail_next);
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	next->cookie = 0;
+	tlr = RTE_PTR_SUB(next, sizeof(struct memarea_objtlr));
+	tlr->cookie = 0;
+#endif
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	struct memarea_objhdr *hdr, *prev, *next;
+
+	if (unlikely(ma == NULL || ptr == NULL))
+		return;
+
+	hdr = RTE_PTR_SUB(ptr, sizeof(struct memarea_objhdr));
+	if (unlikely(!MEMAREA_OBJECT_IS_ALLOCATED(hdr))) {
+		RTE_MEMAREA_LOG(ERR, "detect invalid object in %s!", ma->init.name);
+		return;
+	}
+	memarea_check_cookie(ma, hdr, 1);
+
+	memarea_lock(ma);
+
+	/** 1st: add to avail list. */
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	hdr->cookie = MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE;
+#endif
+	TAILQ_INSERT_HEAD(&ma->avail_list, hdr, avail_next);
+
+	/** 2nd: merge if previous object is avail. */
+	prev = TAILQ_PREV(hdr, memarea_objhdr_list, obj_next);
+	if (prev != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(prev)) {
+		memarea_check_cookie(ma, prev, 0);
+		memarea_merge_node(ma, prev, hdr);
+		hdr = prev;
+	}
+
+	/** 3rd: merge if next object is avail. */
+	next = TAILQ_NEXT(hdr, obj_next);
+	if (next != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(next)) {
+		memarea_check_cookie(ma, next, 0);
+		memarea_merge_node(ma, hdr, next);
+	}
+
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 435dca293f..02b4825461 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -115,6 +115,48 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ *
+ * @return
+ *   - NULL on error. Not enough memory, or invalid arguments (ma is NULL,
+ *     size is 0).
+ *   - Otherwise, the pointer to the allocated object.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), it must be freed to the same memarea which previous
+ * allocated from. The behaviour of rte_memarea_free() is undefined if the
+ * memarea or pointer does not match these requirements.
+ *
+ * @param ma
+ *   The pointer of memarea. If the ma is NULL, the function does nothing.
+ * @param ptr
+ *   The pointer of memory object which need be freed. If the pointer is NULL,
+ *   the function does nothing.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..effbd0b488 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,10 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v14 4/6] test/memarea: support alloc and free API test
  2023-02-09  6:36 ` [PATCH v14 " Chengwen Feng
                     ` (2 preceding siblings ...)
  2023-02-09  6:36   ` [PATCH v14 3/6] memarea: support alloc and free API Chengwen Feng
@ 2023-02-09  6:36   ` Chengwen Feng
  2023-02-09  6:36   ` [PATCH v14 5/6] memarea: support dump API Chengwen Feng
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-02-09  6:36 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_alloc() and rte_memarea_free() API
test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 app/test/test_memarea.c | 135 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 134 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index db5efc4c1b..3e67e59227 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -47,6 +47,12 @@ test_memarea_init_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -95,8 +101,8 @@ test_memarea_create_bad_param(void)
 static int
 test_memarea_create_destroy(void)
 {
+	struct rte_memarea *ma, *src_ma;
 	struct rte_memarea_param init;
-	struct rte_memarea *ma;
 
 	/* test for create with HEAP */
 	test_memarea_init_param(&init);
@@ -113,6 +119,130 @@ test_memarea_create_destroy(void)
 	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with another memarea */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	src_ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(src_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.src_ma = src_ma;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(src_ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	RTE_TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	RTE_TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	RTE_TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	RTE_TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test repeat free */
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	RTE_TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	rte_memarea_free(ma, ptr);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	void *ptr[ALLOC_MAX_NUM];
+	struct rte_memarea *ma;
+	int i;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++)
+		ptr[i] = rte_memarea_alloc(ma, 1);
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	/* test free NULL */
+	rte_memarea_free(ma, NULL);
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -123,6 +253,9 @@ test_memarea(void)
 
 	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
 	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
+	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v14 5/6] memarea: support dump API
  2023-02-09  6:36 ` [PATCH v14 " Chengwen Feng
                     ` (3 preceding siblings ...)
  2023-02-09  6:36   ` [PATCH v14 4/6] test/memarea: support alloc and free API test Chengwen Feng
@ 2023-02-09  6:36   ` Chengwen Feng
  2023-06-22 15:55     ` Burakov, Anatoly
  2023-02-09  6:36   ` [PATCH v14 6/6] test/memarea: support dump API test Chengwen Feng
  2023-06-12 13:53   ` [PATCH v14 0/6] introduce memarea library Ferruh Yigit
  6 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2023-02-09  6:36 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 doc/guides/prog_guide/memarea_lib.rst |  3 +
 lib/memarea/rte_memarea.c             | 98 +++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             | 21 ++++++
 lib/memarea/version.map               |  1 +
 4 files changed, 123 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 55ca4b1166..d9fac69ed3 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -37,6 +37,9 @@ the memarea.
 The ``rte_memarea_free()`` function is used to free one memory object which
 allocated by ``rte_memarea_alloc()``.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 0b53bfe757..55fae30bc3 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -348,3 +348,101 @@ rte_memarea_free(struct rte_memarea *ma, void *ptr)
 
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_MEMAREA)
+		return "memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_algorithm alg)
+{
+	if (alg == RTE_MEMAREA_ALGORITHM_NEXTFIT)
+		return "nextfit";
+	else
+		return "unknown";
+}
+
+static void
+memarea_dump_objects_brief(struct rte_memarea *ma, FILE *f)
+{
+	uint32_t total_objs = 0, total_avail_objs = 0;
+	struct memarea_objhdr *hdr;
+	size_t total_avail_sz = 0;
+
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, 2);
+		total_objs++;
+		if (!MEMAREA_OBJECT_IS_ALLOCATED(hdr)) {
+			total_avail_objs++;
+			total_avail_sz += MEMAREA_OBJECT_GET_SIZE(hdr);
+		}
+	}
+	fprintf(f, "  total-objects: %u\n", total_objs);
+	fprintf(f, "  total-avail-objects: %u\n", total_avail_objs);
+	fprintf(f, "  total-avail-objects-size: 0x%zx\n", total_avail_sz);
+}
+
+static void
+memarea_dump_objects_detail(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_objhdr *hdr;
+	size_t offset;
+	void *ptr;
+
+	fprintf(f, "  objects:\n");
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, 2);
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		offset = RTE_PTR_DIFF(ptr, ma->area_base);
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+		fprintf(f, "    %p off: 0x%zx size: 0x%zx %s\n",
+			ptr, offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#else
+		fprintf(f, "    off: 0x%zx size: 0x%zx %s\n",
+			offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#endif
+	}
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL)
+		return -EINVAL;
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		fprintf(f, "  heap-numa-socket: %d\n", ma->init.numa_socket);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		fprintf(f, "  source-memarea: %s\n", ma->init.src_ma->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	fprintf(f, "  area-base: %p\n", ma->area_base);
+	fprintf(f, "  guard-header: %p\n", ma->guard_hdr);
+#endif
+	memarea_dump_objects_brief(ma, f);
+	if (dump_all)
+		memarea_dump_objects_detail(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 02b4825461..c871accda6 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -157,6 +157,27 @@ void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
 __rte_experimental
 void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned.
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index effbd0b488..9513d91e0b 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 
 	local: *;
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v14 6/6] test/memarea: support dump API test
  2023-02-09  6:36 ` [PATCH v14 " Chengwen Feng
                     ` (4 preceding siblings ...)
  2023-02-09  6:36   ` [PATCH v14 5/6] memarea: support dump API Chengwen Feng
@ 2023-02-09  6:36   ` Chengwen Feng
  2023-06-12 13:53   ` [PATCH v14 0/6] introduce memarea library Ferruh Yigit
  6 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-02-09  6:36 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_dump() API test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 app/test/test_memarea.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 3e67e59227..ad40dbf613 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -246,6 +246,39 @@ test_memarea_alloc_free(void)
 	return 0;
 }
 
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	RTE_TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	RTE_TEST_ASSERT(ret == -EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	ret = rte_memarea_dump(ma, stderr, true);
+	RTE_TEST_ASSERT(ret == 0, "Expected ZERO");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
 static int
 test_memarea(void)
 {
@@ -256,6 +289,7 @@ test_memarea(void)
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_free_fail);
 	MEMAREA_TEST_API_RUN(test_memarea_alloc_free);
+	MEMAREA_TEST_API_RUN(test_memarea_dump);
 
 	return test_memarea_retcode();
 }
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v13 1/6] memarea: introduce memarea library
  2023-02-09  0:04     ` Stephen Hemminger
@ 2023-02-09  6:48       ` fengchengwen
  0 siblings, 0 replies; 222+ messages in thread
From: fengchengwen @ 2023-02-09  6:48 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: david.marchand, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk,
	hofors, thomas, dev

On 2023/2/9 8:04, Stephen Hemminger wrote:
> On Wed, 8 Feb 2023 08:24:47 +0000
> Chengwen Feng <fengchengwen@huawei.com> wrote:
> 
>> +RTE_LOG_REGISTER_DEFAULT(rte_memarea_logtype, INFO);
>> +#define RTE_MEMAREA_LOG(level, ...) \
>> +	rte_log(RTE_LOG_ ## level, rte_memarea_logtype, RTE_FMT("memarea: " \
>> +		RTE_FMT_HEAD(__VA_ARGS__,) "\n", RTE_FMT_TAIL(__VA_ARGS__,)))
>> +
> 
> For me it is easier to read as:
> #define RTE_MEMAREA_LOG(level, fmt, args...) \
> 	rte_log(RTE_LOG_ ## level, rte_memarea_logtype, \
> 		"memarea: " fmt "\n", ## args)
> 
> 
> A common convention is to use "%s: " and __func__.

All fix in v14, thanks.

> .
> 

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v14 0/6] introduce memarea library
  2023-02-09  6:36 ` [PATCH v14 " Chengwen Feng
                     ` (5 preceding siblings ...)
  2023-02-09  6:36   ` [PATCH v14 6/6] test/memarea: support dump API test Chengwen Feng
@ 2023-06-12 13:53   ` Ferruh Yigit
  2023-06-13  9:50     ` fengchengwen
  6 siblings, 1 reply; 222+ messages in thread
From: Ferruh Yigit @ 2023-06-12 13:53 UTC (permalink / raw)
  To: Chengwen Feng, thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

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. The main features are as follows:
> 
> - 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 from another memarea.
> 
> - It supports MT-safe as long as it's specified at creation time.
> 
> Note:
> a) The memarea is oriented towards the application layer, which could
> provides 'region-based memory management' [1] function.
> b) The eal library also provide memory zone/heap management, but these
> are tied to huge pages management.
> 
> [1] https://en.wikipedia.org/wiki/Region-based_memory_management
> 
> Chengwen Feng (6):
>   memarea: introduce memarea library
>   test/memarea: support memarea test
>   memarea: support alloc and free API
>   test/memarea: support alloc and free API test
>   memarea: support dump API
>   test/memarea: support dump API test
> 

Hi Chengwen,

I am aware that patchset it out for a while now, and library has a good
feature set, but I am curious what problem this library solves that
existing APIs can't.
Can you please describe more what is the usecase and motivation of the
feature/library?


I can see it is easy/cheap to destroy a memory region, so is your
problem memory free performance?
Mostly mempool is used to solve this issue, are you planning to use
these APIs on datapath for variable size buffers?


Or is the motivation to be able to run memory management on top of
malloc() allocated memory, if so why?
Hugepages are used for performance reason, if allocated memory is used
for datapath, won't this cause performance degradation.
And if this is the main motivation, can it be an option to add regular
page support to memsegments, this way existing APIs can be used with a
flag, to make it easier for users.


Thanks,
Ferruh


> ---
> v14:
> * address Stephen's comments: RTE_MEMAREA_LOG use easy impl and add
>   __func__ print.
> v13:
> * address Morten's comments: make debug cookies optional, controlled
>   by RTE_LIBRTE_MEMAREA_DEBUG; disabled by default.
> * reduce management data overhead.
> v12:
> * remove rte_memarea_refcnt_update() API which address Dongdong's
>   comments.
> * refine the variable naming.
> * fix some bugs.
> v11:
> * rebase 23.03
> * remove "app/test: add memarea to malloc-perf-autotest" because the
>   two algorithm are not comparable which also address previous
>   comments.
> v10:
> * support windows platform.
> * add rte_memarea.libc perftest to malloc-perf-autotest.
> v9:
> * address Dmitry's comments.
> * drop features of SOURCE_USER and backup memarea mechanism.
> * rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
>   to keep with rte_mbuf_refcnt_update name style.
> * fix memarea perftest compile failed at windows platform.
> * fix spell warning.
> v8:
> * address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
> * small feature patches are combined.
> * enhanced backup memory mechanism.
> * add memarea to malloc-perf-autotest.
> * other tiny naming optimize.
> v7:
> * repost patches as there are spread over different series in patchwork.
> v6:
> * address Mattias's comments.
> v5:
> * fix 09/10 patch spell warning.
> v4:
> * repost patches as there are spread over different series in patchwork.
> v3:
> * add memory source of RTE memory.
> * add algorithm field to facilitate the introduction of new algorithms.
> * fix memarea log don't output problem.
> v2:
> * fix compile issues reported by dpdk-test-report.
> * address Dimitry and Jerin's comments.
> * add no MT-safe test.
> 
>  MAINTAINERS                            |   6 +
>  app/test/meson.build                   |   2 +
>  app/test/test_memarea.c                | 297 ++++++++++++++++
>  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  |  55 +++
>  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              | 448 +++++++++++++++++++++++++
>  lib/memarea/rte_memarea.h              | 185 ++++++++++
>  lib/memarea/version.map                |  15 +
>  lib/meson.build                        |   1 +
>  14 files changed, 1087 insertions(+), 1 deletion(-)
>  create mode 100644 app/test/test_memarea.c
>  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
> 


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v14 0/6] introduce memarea library
  2023-06-12 13:53   ` [PATCH v14 0/6] introduce memarea library Ferruh Yigit
@ 2023-06-13  9:50     ` fengchengwen
  2023-06-13 11:04       ` Burakov, Anatoly
  0 siblings, 1 reply; 222+ messages in thread
From: fengchengwen @ 2023-06-13  9:50 UTC (permalink / raw)
  To: Ferruh Yigit, thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

Hi Ferruh,

On 2023/6/12 21:53, Ferruh Yigit wrote:
> 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. The main features are as follows:
>>
>> - 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 from another memarea.
>>
>> - It supports MT-safe as long as it's specified at creation time.
>>
>> Note:
>> a) The memarea is oriented towards the application layer, which could
>> provides 'region-based memory management' [1] function.
>> b) The eal library also provide memory zone/heap management, but these
>> are tied to huge pages management.
>>
>> [1] https://en.wikipedia.org/wiki/Region-based_memory_management
>>
>> Chengwen Feng (6):
>>   memarea: introduce memarea library
>>   test/memarea: support memarea test
>>   memarea: support alloc and free API
>>   test/memarea: support alloc and free API test
>>   memarea: support dump API
>>   test/memarea: support dump API test
>>
> 
> Hi Chengwen,
> 
> I am aware that patchset it out for a while now, and library has a good
> feature set, but I am curious what problem this library solves that
> existing APIs can't.
> Can you please describe more what is the usecase and motivation of the
> feature/library?

Please see the reply below.

> 
> 
> I can see it is easy/cheap to destroy a memory region, so is your
> problem memory free performance?
> Mostly mempool is used to solve this issue, are you planning to use
> these APIs on datapath for variable size buffers?

Yes, it could use on datapath.

Now DPDK is accelerating more areas (not limited to pure packet processing),
The applications have variable size buffers malloc/free requirement on datapath
(e.g. video app decode/encode).

One option is to use rte_malloc, but the rte_malloc is not target for datapath
because it's implementation relies on a global lock.

The optimization direction:
1) optimize rte_malloc API, use something like core-cache tech.
2) change the mindset, make each application session use its own memory region
   (as this library provides), e.g. if have three video decocder run on dpdk
   thread, application could create three memarea for each.
3) hybrid management[1], which use both 1&2.

> 
> 
> Or is the motivation to be able to run memory management on top of
> malloc() allocated memory, if so why?
> Hugepages are used for performance reason, if allocated memory is used
> for datapath, won't this cause performance degradation.

Hugepages is a dessert, it need specific API to management (unless the transparent huge page).

Why provide the memarea over malloc because: the memarea is programmer friendly, we could
alloc one region, and do many alloc subblock, at last free the region.

The application decide whether to use such feature (memarea over malloc).

> And if this is the main motivation, can it be an option to add regular
> page support to memsegments, this way existing APIs can be used with a
> flag, to make it easier for users.

Also, I do some investigation in DPDK recently and found the mlx5 use mlx5_ipool to management
its device memory, I think the memarea could be extended to support such usecase.

So I think the memarea library could use both control and datapath, and also for device memory
management.

[1] https://www.youtube.com/watch?v=YghmGLyqrVg

Thanks.

> 
> 
> Thanks,
> Ferruh
> 
> 
>> ---
>> v14:
>> * address Stephen's comments: RTE_MEMAREA_LOG use easy impl and add
>>   __func__ print.
>> v13:
>> * address Morten's comments: make debug cookies optional, controlled
>>   by RTE_LIBRTE_MEMAREA_DEBUG; disabled by default.
>> * reduce management data overhead.
>> v12:
>> * remove rte_memarea_refcnt_update() API which address Dongdong's
>>   comments.
>> * refine the variable naming.
>> * fix some bugs.
>> v11:
>> * rebase 23.03
>> * remove "app/test: add memarea to malloc-perf-autotest" because the
>>   two algorithm are not comparable which also address previous
>>   comments.
>> v10:
>> * support windows platform.
>> * add rte_memarea.libc perftest to malloc-perf-autotest.
>> v9:
>> * address Dmitry's comments.
>> * drop features of SOURCE_USER and backup memarea mechanism.
>> * rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
>>   to keep with rte_mbuf_refcnt_update name style.
>> * fix memarea perftest compile failed at windows platform.
>> * fix spell warning.
>> v8:
>> * address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
>> * small feature patches are combined.
>> * enhanced backup memory mechanism.
>> * add memarea to malloc-perf-autotest.
>> * other tiny naming optimize.
>> v7:
>> * repost patches as there are spread over different series in patchwork.
>> v6:
>> * address Mattias's comments.
>> v5:
>> * fix 09/10 patch spell warning.
>> v4:
>> * repost patches as there are spread over different series in patchwork.
>> v3:
>> * add memory source of RTE memory.
>> * add algorithm field to facilitate the introduction of new algorithms.
>> * fix memarea log don't output problem.
>> v2:
>> * fix compile issues reported by dpdk-test-report.
>> * address Dimitry and Jerin's comments.
>> * add no MT-safe test.
>>
>>  MAINTAINERS                            |   6 +
>>  app/test/meson.build                   |   2 +
>>  app/test/test_memarea.c                | 297 ++++++++++++++++
>>  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  |  55 +++
>>  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              | 448 +++++++++++++++++++++++++
>>  lib/memarea/rte_memarea.h              | 185 ++++++++++
>>  lib/memarea/version.map                |  15 +
>>  lib/meson.build                        |   1 +
>>  14 files changed, 1087 insertions(+), 1 deletion(-)
>>  create mode 100644 app/test/test_memarea.c
>>  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
>>
> 
> .
> 

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v14 0/6] introduce memarea library
  2023-06-13  9:50     ` fengchengwen
@ 2023-06-13 11:04       ` Burakov, Anatoly
  2023-06-13 12:46         ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: Burakov, Anatoly @ 2023-06-13 11:04 UTC (permalink / raw)
  To: fengchengwen, Ferruh Yigit, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

On 6/13/2023 10:50 AM, fengchengwen wrote:
> Hi Ferruh,
> 
> On 2023/6/12 21:53, Ferruh Yigit wrote:
>> 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. The main features are as follows:
>>>
>>> - 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 from another memarea.
>>>
>>> - It supports MT-safe as long as it's specified at creation time.
>>>
>>> Note:
>>> a) The memarea is oriented towards the application layer, which could
>>> provides 'region-based memory management' [1] function.
>>> b) The eal library also provide memory zone/heap management, but these
>>> are tied to huge pages management.
>>>
>>> [1] https://en.wikipedia.org/wiki/Region-based_memory_management
>>>
>>> Chengwen Feng (6):
>>>    memarea: introduce memarea library
>>>    test/memarea: support memarea test
>>>    memarea: support alloc and free API
>>>    test/memarea: support alloc and free API test
>>>    memarea: support dump API
>>>    test/memarea: support dump API test
>>>
>>

Hi,

 From my understanding, this library is not intended to support 
secondary process use cases?

-- 
Thanks,
Anatoly


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v14 0/6] introduce memarea library
  2023-06-13 11:04       ` Burakov, Anatoly
@ 2023-06-13 12:46         ` fengchengwen
  0 siblings, 0 replies; 222+ messages in thread
From: fengchengwen @ 2023-06-13 12:46 UTC (permalink / raw)
  To: Burakov, Anatoly, Ferruh Yigit, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

On 2023/6/13 19:04, Burakov, Anatoly wrote:
> On 6/13/2023 10:50 AM, fengchengwen wrote:
>> Hi Ferruh,
>>
>> On 2023/6/12 21:53, Ferruh Yigit wrote:
>>> 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. The main features are as follows:
>>>>
>>>> - 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 from another memarea.
>>>>
>>>> - It supports MT-safe as long as it's specified at creation time.
>>>>
>>>> Note:
>>>> a) The memarea is oriented towards the application layer, which could
>>>> provides 'region-based memory management' [1] function.
>>>> b) The eal library also provide memory zone/heap management, but these
>>>> are tied to huge pages management.
>>>>
>>>> [1] https://en.wikipedia.org/wiki/Region-based_memory_management
>>>>
>>>> Chengwen Feng (6):
>>>>    memarea: introduce memarea library
>>>>    test/memarea: support memarea test
>>>>    memarea: support alloc and free API
>>>>    test/memarea: support alloc and free API test
>>>>    memarea: support dump API
>>>>    test/memarea: support dump API test
>>>>
>>>
> 
> Hi,
> 
> From my understanding, this library is not intended to support secondary process use cases?

Yes, it's not supported in the current version, we could extend to support if necessary.

> 

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v14 1/6] memarea: introduce memarea library
  2023-02-09  6:36   ` [PATCH v14 1/6] memarea: " Chengwen Feng
@ 2023-06-21 10:52     ` Burakov, Anatoly
  2023-06-21 12:05     ` Burakov, Anatoly
  1 sibling, 0 replies; 222+ messages in thread
From: Burakov, Anatoly @ 2023-06-21 10:52 UTC (permalink / raw)
  To: Chengwen Feng, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

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.

-- 
Thanks,
Anatoly


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v14 2/6] test/memarea: support memarea test
  2023-02-09  6:36   ` [PATCH v14 2/6] test/memarea: support memarea test Chengwen Feng
@ 2023-06-21 11:00     ` Burakov, Anatoly
  0 siblings, 0 replies; 222+ messages in thread
From: Burakov, Anatoly @ 2023-06-21 11:00 UTC (permalink / raw)
  To: Chengwen Feng, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

On 2/9/2023 6:36 AM, Chengwen Feng wrote:
> This patch supports memarea test of 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>
> ---

Hi,

> +
> +static int
> +test_memarea(void)
> +{
> +	test_memarea_prepare();
> +
> +	MEMAREA_TEST_API_RUN(test_memarea_create_bad_param);
> +	MEMAREA_TEST_API_RUN(test_memarea_create_destroy);

It looks a lot like you're reimplementing functionality that is already 
present in the test app, as we already have infrastructure to support 
test setup, teardown, and running different test suites (see e.g. 
fbarray or EAL flags tests for example). Is there any particular reason 
why you didn't go for this approach?

-- 
Thanks,
Anatoly


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v14 1/6] memarea: introduce memarea library
  2023-02-09  6:36   ` [PATCH v14 1/6] memarea: " Chengwen Feng
  2023-06-21 10:52     ` Burakov, Anatoly
@ 2023-06-21 12:05     ` Burakov, Anatoly
  1 sibling, 0 replies; 222+ messages in thread
From: Burakov, Anatoly @ 2023-06-21 12:05 UTC (permalink / raw)
  To: Chengwen Feng, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

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.

-- 
Thanks,
Anatoly


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v14 3/6] memarea: support alloc and free API
  2023-02-09  6:36   ` [PATCH v14 3/6] memarea: support alloc and free API Chengwen Feng
@ 2023-06-22 15:29     ` Burakov, Anatoly
  0 siblings, 0 replies; 222+ messages in thread
From: Burakov, Anatoly @ 2023-06-22 15:29 UTC (permalink / raw)
  To: Chengwen Feng, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

On 2/9/2023 6:36 AM, Chengwen Feng wrote:
> This patch supports rte_memarea_alloc() and rte_memarea_free() API.
> 
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
> Acked-by: Morten Brørup <mb@smartsharesystems.com>
> ---

General note: this patchset could benefit from a bit more comments. I 
don't suggest commenting every line, but at least more comments denoting 
various logical steps (like you have in `rte_memarea_free`) would be 
nice to have.


>   #include <rte_common.h>
>   #include <rte_log.h>
> @@ -88,6 +90,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
>   					init->numa_socket);
>   	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
>   		ptr = memarea_alloc_from_libc(init->total_sz);
> +	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
> +		ptr = rte_memarea_alloc(init->src_ma, init->total_sz);

Why `if` not `switch`?

>   
>   	return ptr;
>   }
> @@ -99,6 +103,8 @@ memarea_free_area(const struct rte_memarea_param *init, void *ptr)
>   		rte_free(ptr);
>   	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
>   		free(ptr);
> +	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
> +		rte_memarea_free(init->src_ma, ptr);

Similarly here: why `if` not `switch`?

> +static inline void
> +memarea_add_node(struct rte_memarea *ma, struct memarea_objhdr *hdr, size_t alloc_sz)
> +{
> +#ifdef RTE_LIBRTE_MEMAREA_DEBUG
> +	struct memarea_objtlr *cur_tlr;
> +#endif
> +	struct memarea_objhdr *new_hdr;
> +
> +#ifdef RTE_LIBRTE_MEMAREA_DEBUG
> +	cur_tlr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz);
> +	cur_tlr->cookie = MEMAREA_OBJECT_TRAILER_COOKIE;
> +	new_hdr = RTE_PTR_ADD(cur_tlr, sizeof(struct memarea_objtlr));
> +	new_hdr->cookie = MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE;
> +#else
> +	new_hdr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz);
> +#endif
> +	TAILQ_INSERT_AFTER(&ma->obj_list, hdr, new_hdr, obj_next);
> +	TAILQ_INSERT_AFTER(&ma->avail_list, hdr, new_hdr, avail_next);
> +}

It seems to me that this function isn't "adding" node but rather is 
splitting the `hdr` into two nodes. This is nitpicking, but I feel like 
this part could be clearer semantically (splitting the function, adding 
comments, some other way...).

> +
> +void *
> +rte_memarea_alloc(struct rte_memarea *ma, size_t size)
> +{
> +	size_t align_sz = RTE_ALIGN(size, MEMAREA_OBJECT_SIZE_ALIGN);
> +	struct memarea_objhdr *hdr;
> +	size_t avail_sz;
> +	void *ptr = NULL;
> +
> +	if (unlikely(ma == NULL || size == 0 || align_sz < size))
> +		return ptr;

It would be nice if API also set rte_errno to indicate what kind of 
error has happened.

> +
> +	memarea_lock(ma);
> +	TAILQ_FOREACH(hdr, &ma->avail_list, avail_next) {
> +		memarea_check_cookie(ma, hdr, 0);
> +		avail_sz = MEMAREA_OBJECT_GET_SIZE(hdr);
> +		if (avail_sz < align_sz)
> +			continue;
> +		if (memarea_whether_add_node(avail_sz, align_sz))
> +			memarea_add_node(ma, hdr, align_sz);

I didn't get this at first, which means it needs comments :) 
Specifically, it seems to me that we're only "adding" a node when we can 
comfortably split it. So, in addition to comments documenting the above, 
perhaps the above functions should also be called differently? Like 
`memarea_can_split()` and `memarea_split`? IMO it'd communicate the 
intent better (unless I misunderstood the intent, that is!).

> +		TAILQ_REMOVE(&ma->avail_list, hdr, avail_next);
> +		MEMAREA_OBJECT_MARK_ALLOCATED(hdr);
> +#ifdef RTE_LIBRTE_MEMAREA_DEBUG
> +		hdr->cookie = MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE;
> +#endif
> +		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
> +		break;
> +	}
> +	memarea_unlock(ma);
> +
> +	return ptr;

It seems that it's possible to reach the end of the loop and return NULL 
as `ptr`, without any error. I would suggest setting rte_errno to 
`ENOMEM` initially, and clearing it when we find a suitable element.

> +}
> +
> +static inline void
> +memarea_merge_node(struct rte_memarea *ma, struct memarea_objhdr *curr,
> +		   struct memarea_objhdr *next)
> +{
> +#ifdef RTE_LIBRTE_MEMAREA_DEBUG
> +	struct memarea_objtlr *tlr;
> +#endif
> +	RTE_SET_USED(curr);
> +	TAILQ_REMOVE(&ma->obj_list, next, obj_next);
> +	TAILQ_REMOVE(&ma->avail_list, next, avail_next);
> +#ifdef RTE_LIBRTE_MEMAREA_DEBUG
> +	next->cookie = 0;
> +	tlr = RTE_PTR_SUB(next, sizeof(struct memarea_objtlr));
> +	tlr->cookie = 0;
> +#endif
> +}
> +
> +void
> +rte_memarea_free(struct rte_memarea *ma, void *ptr)
> +{
> +	struct memarea_objhdr *hdr, *prev, *next;
> +
> +	if (unlikely(ma == NULL || ptr == NULL))
> +		return;
> +
> +	hdr = RTE_PTR_SUB(ptr, sizeof(struct memarea_objhdr));
> +	if (unlikely(!MEMAREA_OBJECT_IS_ALLOCATED(hdr))) {

Here and above - I question the value of using `unlikely` here. Are 
there any numbers to prove these are useful?

> +		RTE_MEMAREA_LOG(ERR, "detect invalid object in %s!", ma->init.name);
> +		return;
> +	}
> +	memarea_check_cookie(ma, hdr, 1);
> +
> +	memarea_lock(ma);
> +
> +	/** 1st: add to avail list. */
> +#ifdef RTE_LIBRTE_MEMAREA_DEBUG
> +	hdr->cookie = MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE;
> +#endif
> +	TAILQ_INSERT_HEAD(&ma->avail_list, hdr, avail_next);
> +
> +	/** 2nd: merge if previous object is avail. */
> +	prev = TAILQ_PREV(hdr, memarea_objhdr_list, obj_next);
> +	if (prev != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(prev)) {
> +		memarea_check_cookie(ma, prev, 0);
> +		memarea_merge_node(ma, prev, hdr);
> +		hdr = prev;
> +	}
> +
> +	/** 3rd: merge if next object is avail. */
> +	next = TAILQ_NEXT(hdr, obj_next);
> +	if (next != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(next)) {
> +		memarea_check_cookie(ma, next, 0);
> +		memarea_merge_node(ma, hdr, next);
> +	}
> +
> +	memarea_unlock(ma);
> +}

This function is an example of how I would like to see other functions: 
good comments to denote logical blocks, clear and concise.

-- 
Thanks,
Anatoly


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v14 5/6] memarea: support dump API
  2023-02-09  6:36   ` [PATCH v14 5/6] memarea: support dump API Chengwen Feng
@ 2023-06-22 15:55     ` Burakov, Anatoly
  2023-06-28  1:25       ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: Burakov, Anatoly @ 2023-06-22 15:55 UTC (permalink / raw)
  To: Chengwen Feng, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

On 2/9/2023 6:36 AM, Chengwen Feng wrote:
> This patch supports rte_memarea_dump() API which could be used for
> debug.
> 
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
> Acked-by: Morten Brørup <mb@smartsharesystems.com>
> ---

Provisionally,

Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>

As long as below is addressed.

> +static void
> +memarea_dump_objects_detail(struct rte_memarea *ma, FILE *f)
> +{
> +	struct memarea_objhdr *hdr;
> +	size_t offset;
> +	void *ptr;
> +
> +	fprintf(f, "  objects:\n");
> +	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
> +		if (hdr == ma->guard_hdr)
> +			break;
> +		memarea_check_cookie(ma, hdr, 2);
> +		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
> +		offset = RTE_PTR_DIFF(ptr, ma->area_base);
> +#ifdef RTE_LIBRTE_MEMAREA_DEBUG
> +		fprintf(f, "    %p off: 0x%zx size: 0x%zx %s\n",
> +			ptr, offset, MEMAREA_OBJECT_GET_SIZE(hdr),
> +			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
> +#else
> +		fprintf(f, "    off: 0x%zx size: 0x%zx %s\n",
> +			offset, MEMAREA_OBJECT_GET_SIZE(hdr),
> +			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
> +#endif
> +	}.
> +}
> +
> +int
> +rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
> +{
> +	if (ma == NULL || f == NULL)
> +		return -EINVAL;

I feel like the API is inconsistent in this way. I would suggest picking 
a method of error reporting, and sticking with it. I would suggest 
returning 0/-1 or ptr/NULL with rte_errno set to indicate error, as that 
is how most libraries in DPDK behave.

-- 
Thanks,
Anatoly


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v14 5/6] memarea: support dump API
  2023-06-22 15:55     ` Burakov, Anatoly
@ 2023-06-28  1:25       ` fengchengwen
  2023-06-28  1:39         ` Thomas Monjalon
  0 siblings, 1 reply; 222+ messages in thread
From: fengchengwen @ 2023-06-28  1:25 UTC (permalink / raw)
  To: Burakov, Anatoly, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

Hi Anatoly,

  Thanks for your review, a lot of valuable advice.

  PS: This library stays for a long time, want to hear TB's opinion: whether to continue or stop.
      If continue I will push a new version.

Thanks.

On 2023/6/22 23:55, Burakov, Anatoly wrote:
> On 2/9/2023 6:36 AM, Chengwen Feng wrote:
>> This patch supports rte_memarea_dump() API which could be used for
>> debug.
>>
>> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
>> Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
>> Acked-by: Morten Brørup <mb@smartsharesystems.com>
>> ---
> 
> Provisionally,
> 
> Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
> 
> As long as below is addressed.
> 
>> +static void
>> +memarea_dump_objects_detail(struct rte_memarea *ma, FILE *f)
>> +{
>> +    struct memarea_objhdr *hdr;
>> +    size_t offset;
>> +    void *ptr;
>> +
>> +    fprintf(f, "  objects:\n");
>> +    TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
>> +        if (hdr == ma->guard_hdr)
>> +            break;
>> +        memarea_check_cookie(ma, hdr, 2);
>> +        ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
>> +        offset = RTE_PTR_DIFF(ptr, ma->area_base);
>> +#ifdef RTE_LIBRTE_MEMAREA_DEBUG
>> +        fprintf(f, "    %p off: 0x%zx size: 0x%zx %s\n",
>> +            ptr, offset, MEMAREA_OBJECT_GET_SIZE(hdr),
>> +            MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
>> +#else
>> +        fprintf(f, "    off: 0x%zx size: 0x%zx %s\n",
>> +            offset, MEMAREA_OBJECT_GET_SIZE(hdr),
>> +            MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
>> +#endif
>> +    }.
>> +}
>> +
>> +int
>> +rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
>> +{
>> +    if (ma == NULL || f == NULL)
>> +        return -EINVAL;
> 
> I feel like the API is inconsistent in this way. I would suggest picking a method of error reporting, and sticking with it. I would suggest returning 0/-1 or ptr/NULL with rte_errno set to indicate error, as that is how most libraries in DPDK behave.
> 

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v14 5/6] memarea: support dump API
  2023-06-28  1:25       ` fengchengwen
@ 2023-06-28  1:39         ` Thomas Monjalon
  2023-06-28  1:48           ` fengchengwen
  2023-07-03 10:29           ` Burakov, Anatoly
  0 siblings, 2 replies; 222+ messages in thread
From: Thomas Monjalon @ 2023-06-28  1:39 UTC (permalink / raw)
  To: Burakov, Anatoly, fengchengwen
  Cc: david.marchand, dev, mb, dmitry.kozliuk, jerinjacobk, hofors,
	stephen, techboard

28/06/2023 03:25, fengchengwen:
> Hi Anatoly,
> 
>   Thanks for your review, a lot of valuable advice.
> 
>   PS: This library stays for a long time, want to hear TB's opinion: whether to continue or stop.
>       If continue I will push a new version.

Would you be able to attend the techboard meeting today (3pm UTC)?
	https://core.dpdk.org/techboard/minutes/
We could discuss the value of this library.
Anatoly, would you be able to prepare some arguments as well?



^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v14 5/6] memarea: support dump API
  2023-06-28  1:39         ` Thomas Monjalon
@ 2023-06-28  1:48           ` fengchengwen
  2023-07-03 10:29           ` Burakov, Anatoly
  1 sibling, 0 replies; 222+ messages in thread
From: fengchengwen @ 2023-06-28  1:48 UTC (permalink / raw)
  To: Thomas Monjalon, Burakov, Anatoly
  Cc: david.marchand, dev, mb, dmitry.kozliuk, jerinjacobk, hofors,
	stephen, techboard

On 2023/6/28 9:39, Thomas Monjalon wrote:
> 28/06/2023 03:25, fengchengwen:
>> Hi Anatoly,
>>
>>   Thanks for your review, a lot of valuable advice.
>>
>>   PS: This library stays for a long time, want to hear TB's opinion: whether to continue or stop.
>>       If continue I will push a new version.
> 
> Would you be able to attend the techboard meeting today (3pm UTC)?

Great, I will attend, Thanks.

> 	https://core.dpdk.org/techboard/minutes/
> We could discuss the value of this library.
> Anatoly, would you be able to prepare some arguments as well?
> 
> 
> .
> 

^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v14 5/6] memarea: support dump API
  2023-06-28  1:39         ` Thomas Monjalon
  2023-06-28  1:48           ` fengchengwen
@ 2023-07-03 10:29           ` Burakov, Anatoly
  1 sibling, 0 replies; 222+ messages in thread
From: Burakov, Anatoly @ 2023-07-03 10:29 UTC (permalink / raw)
  To: Thomas Monjalon, fengchengwen
  Cc: david.marchand, dev, mb, dmitry.kozliuk, jerinjacobk, hofors,
	stephen, techboard

On 6/28/2023 2:39 AM, Thomas Monjalon wrote:
> 28/06/2023 03:25, fengchengwen:
>> Hi Anatoly,
>>
>>    Thanks for your review, a lot of valuable advice.
>>
>>    PS: This library stays for a long time, want to hear TB's opinion: whether to continue or stop.
>>        If continue I will push a new version.
> 
> Would you be able to attend the techboard meeting today (3pm UTC)?
> 	https://core.dpdk.org/techboard/minutes/
> We could discuss the value of this library.
> Anatoly, would you be able to prepare some arguments as well?
> 
> 

Apologies, I missed this email :( I don't see any minutes, there was no 
techboard?

As for "arguments", I have no strong opinions on whether this library 
should be added or not. It seems like a viable alternative to using 
mempools when a certain amount of scratch space is needed for whatever 
reasons (I remember having discussions about current allocator being too 
heavyweight for this kind of scenario), so to me, it's not that I'm 
particularly fond of the library, it's moreso that I think that it 
sounds useful and I don't have any strong objections to its inclusion.

-- 
Thanks,
Anatoly


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v15 0/6] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (18 preceding siblings ...)
  2023-02-09  6:36 ` [PATCH v14 " Chengwen Feng
@ 2023-07-09 13:00 ` Chengwen Feng
  2023-07-09 13:00   ` [PATCH v15 1/6] memarea: " Chengwen Feng
                     ` (5 more replies)
  2023-07-10  6:49 ` [PATCH v16 0/6] introduce memarea library Chengwen Feng
                   ` (3 subsequent siblings)
  23 siblings, 6 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-09 13:00 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- 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 from another memarea.

- It supports MT-safe as long as it's specified at creation time.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Chengwen Feng (6):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc and free API
  test/memarea: support alloc and free API test
  memarea: support dump API
  test/memarea: support dump API test

---
v15:
* rebase 23.07
* address Anatoly's mostly comments: use rte_errno, abstract cookie
  helper, more comment about critical function, rename add with split,
  doc limitation.
v14:
* address Stephen's comments: RTE_MEMAREA_LOG use easy impl and add
  __func__ print.
v13:
* address Morten's comments: make debug cookies optional, controlled
  by RTE_LIBRTE_MEMAREA_DEBUG; disabled by default.
* reduce management data overhead.
v12:
* remove rte_memarea_refcnt_update() API which address Dongdong's
  comments.
* refine the variable naming.
* fix some bugs.
v11:
* rebase 23.03
* remove "app/test: add memarea to malloc-perf-autotest" because the
  two algorithm are not comparable which also address previous
  comments.
v10:
* support windows platform.
* add rte_memarea.libc perftest to malloc-perf-autotest.
v9:
* address Dmitry's comments.
* drop features of SOURCE_USER and backup memarea mechanism.
* rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
  to keep with rte_mbuf_refcnt_update name style.
* fix memarea perftest compile failed at windows platform.
* fix spell warning.
v8:
* address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
* small feature patches are combined.
* enhanced backup memory mechanism.
* add memarea to malloc-perf-autotest.
* other tiny naming optimize.
v7:
* repost patches as there are spread over different series in patchwork.
v6:
* address Mattias's comments.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 374 +++++++++++++++++++
 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  |  57 +++
 doc/guides/rel_notes/release_23_07.rst |   6 +
 lib/memarea/memarea_private.h          |  96 +++++
 lib/memarea/meson.build                |  10 +
 lib/memarea/rte_memarea.c              | 479 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 202 +++++++++++
 lib/memarea/version.map                |  15 +
 lib/meson.build                        |   1 +
 14 files changed, 1252 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v15 1/6] memarea: introduce memarea library
  2023-07-09 13:00 ` [PATCH v15 " Chengwen Feng
@ 2023-07-09 13:00   ` Chengwen Feng
  2023-07-09 19:46     ` Stephen Hemminger
  2023-07-09 13:00   ` [PATCH v15 2/6] test/memarea: support memarea test Chengwen Feng
                     ` (4 subsequent siblings)
  5 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2023-07-09 13:00 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

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  |  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 <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>
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 <rte_memarea.h>
+
+#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 <stdio.h>
+#include <stdlib.h>
+
+#include <rte_common.h>
+#include <rte_errno.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)
+{
+	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 <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_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


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v15 2/6] test/memarea: support memarea test
  2023-07-09 13:00 ` [PATCH v15 " Chengwen Feng
  2023-07-09 13:00   ` [PATCH v15 1/6] memarea: " Chengwen Feng
@ 2023-07-09 13:00   ` Chengwen Feng
  2023-07-09 13:00   ` [PATCH v15 3/6] memarea: support alloc and free API Chengwen Feng
                     ` (3 subsequent siblings)
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-09 13:00 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports memarea test of 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             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 122 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 125 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 93de0876f1..cc082ae821 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1616,6 +1616,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index 3e0a2360a3..5e3012328d 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -83,6 +83,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -200,6 +201,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..90117caa38
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_errno.h>
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#include "test.h"
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+static void
+test_memarea_init_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "autotest");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid source */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for total_sz */
+	test_memarea_init_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for memarea NULL */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for algorithm invalid */
+	test_memarea_init_param(&init);
+	init.alg = RTE_MEMAREA_ALGORITHM_NEXTFIT + 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	struct rte_memarea *ma;
+	struct rte_memarea_param init;
+
+	rte_errno = 0;
+
+	/* test for create with HEAP */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.heap.socket_id = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static struct unit_test_suite memarea_test_suite  = {
+	.suite_name = "Memarea Unit Test Suite",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+		TEST_CASE(test_memarea_create_bad_param),
+		TEST_CASE(test_memarea_create_destroy),
+
+		TEST_CASES_END() /**< NULL terminate unit test array */
+	}
+};
+
+static int
+test_memarea(void)
+{
+	return unit_test_suite_runner(&memarea_test_suite);
+}
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v15 3/6] memarea: support alloc and free API
  2023-07-09 13:00 ` [PATCH v15 " Chengwen Feng
  2023-07-09 13:00   ` [PATCH v15 1/6] memarea: " Chengwen Feng
  2023-07-09 13:00   ` [PATCH v15 2/6] test/memarea: support memarea test Chengwen Feng
@ 2023-07-09 13:00   ` Chengwen Feng
  2023-07-09 13:00   ` [PATCH v15 4/6] test/memarea: support alloc and free API test Chengwen Feng
                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-09 13:00 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_alloc() and rte_memarea_free() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   6 +
 lib/memarea/memarea_private.h         |  10 ++
 lib/memarea/rte_memarea.c             | 164 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  44 +++++++
 lib/memarea/version.map               |   2 +
 5 files changed, 226 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index bf19090294..157baf3c7e 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,12 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 61dc518777..43286e413d 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -22,10 +22,20 @@
 #define MEMAREA_OBJECT_GET_SIZE(hdr) \
 		((uintptr_t)TAILQ_NEXT((hdr), obj_next) - (uintptr_t)(hdr) - \
 		 sizeof(struct memarea_objhdr) - sizeof(struct memarea_objtlr))
+#define MEMAREA_SPLIT_OBJECT_MIN_SIZE \
+		(sizeof(struct memarea_objhdr) + MEMAREA_OBJECT_SIZE_ALIGN + \
+		 sizeof(struct memarea_objtlr))
+#define MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz) \
+		RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz + \
+			    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))
+#define MEMAREA_SPLIT_OBJECT_MIN_SIZE \
+		(sizeof(struct memarea_objhdr) + MEMAREA_OBJECT_SIZE_ALIGN)
+#define MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz) \
+		RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz)
 #endif
 
 struct memarea_objhdr {
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 70ac08a572..393068f008 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,8 +2,10 @@
  * Copyright(c) 2023 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_errno.h>
@@ -88,6 +90,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 					init->heap.socket_id);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		ptr = memarea_alloc_from_libc(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		ptr = rte_memarea_alloc(init->ma.src, init->total_sz);
 
 	return ptr;
 }
@@ -99,6 +103,8 @@ memarea_free_area(const struct rte_memarea_param *init, void *ptr)
 		rte_free(ptr);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ptr);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		rte_memarea_free(init->ma.src, ptr);
 }
 
 /**
@@ -213,3 +219,161 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(&ma->init, ma->area_base);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+/**
+ * Check cookie or panic.
+ *
+ * @param status
+ *   - 0: object is supposed to be available.
+ *   - 1: object is supposed to be allocated.
+ *   - 2: just check that cookie is valid (available or allocated).
+ */
+static inline void
+memarea_check_cookie(const struct rte_memarea *ma, const struct memarea_objhdr *hdr, int status)
+{
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	static const char *const str[] = { "PASS", "FAILED" };
+	struct memarea_objtlr *tlr;
+	bool hdr_fail, tlr_fail;
+
+	if (hdr == ma->guard_hdr)
+		return;
+
+	tlr = RTE_PTR_SUB(TAILQ_NEXT(hdr, obj_next), sizeof(struct memarea_objtlr));
+	hdr_fail = (status == 0 && hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE) ||
+		   (status == 1 && hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE) ||
+		   (status == 2 && (hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE &&
+				    hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE));
+	tlr_fail = (tlr->cookie != MEMAREA_OBJECT_TRAILER_COOKIE);
+	if (!hdr_fail && !tlr_fail)
+		return;
+
+	rte_panic("MEMAREA: %s check cookies failed! addr-%p header-cookie<0x%" PRIx64 " %s> trailer-cookie<0x%" PRIx64 " %s>\n",
+		ma->init.name, RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr)),
+		hdr->cookie, str[hdr_fail], tlr->cookie, str[tlr_fail]);
+#else
+	RTE_SET_USED(ma);
+	RTE_SET_USED(hdr);
+	RTE_SET_USED(status);
+#endif
+}
+
+static inline void
+memarea_split_object(struct rte_memarea *ma, struct memarea_objhdr *hdr, size_t alloc_sz)
+{
+	struct memarea_objhdr *split_hdr;
+
+	split_hdr = MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz);
+	memarea_set_cookie(split_hdr, 2);
+	TAILQ_INSERT_AFTER(&ma->obj_list, hdr, split_hdr, obj_next);
+	TAILQ_INSERT_AFTER(&ma->avail_list, hdr, split_hdr, avail_next);
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size)
+{
+	size_t align_sz = RTE_ALIGN(size, MEMAREA_OBJECT_SIZE_ALIGN);
+	struct memarea_objhdr *hdr;
+	size_t avail_sz;
+	void *ptr = NULL;
+
+	if (ma == NULL || size == 0 || align_sz < size) {
+		rte_errno = EINVAL;
+		return ptr;
+	}
+
+	memarea_lock(ma);
+
+	/** traverse every available object, return the first satisfied one. */
+	TAILQ_FOREACH(hdr, &ma->avail_list, avail_next) {
+		/** 1st: check whether the object size meets. */
+		memarea_check_cookie(ma, hdr, 0);
+		avail_sz = MEMAREA_OBJECT_GET_SIZE(hdr);
+		if (avail_sz < align_sz)
+			continue;
+
+		/** 2nd: if the object size is too long, a new object can be split. */
+		if (avail_sz - align_sz > MEMAREA_SPLIT_OBJECT_MIN_SIZE)
+			memarea_split_object(ma, hdr, align_sz);
+
+		/** 3rd: allocate successful. */
+		TAILQ_REMOVE(&ma->avail_list, hdr, avail_next);
+		MEMAREA_OBJECT_MARK_ALLOCATED(hdr);
+		memarea_set_cookie(hdr, 1);
+
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		break;
+	}
+
+	memarea_unlock(ma);
+
+	if (ptr == NULL)
+		rte_errno = ENOMEM;
+	return ptr;
+}
+
+static inline void
+memarea_merge_object(struct rte_memarea *ma, struct memarea_objhdr *curr,
+		   struct memarea_objhdr *next)
+{
+	RTE_SET_USED(curr);
+	TAILQ_REMOVE(&ma->obj_list, next, obj_next);
+	TAILQ_REMOVE(&ma->avail_list, next, avail_next);
+	memarea_set_cookie(next, 4);
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	struct memarea_objhdr *hdr, *prev, *next;
+
+	if (ma == NULL || ptr == NULL) {
+		rte_errno = EINVAL;
+		return;
+	}
+
+	hdr = RTE_PTR_SUB(ptr, sizeof(struct memarea_objhdr));
+	if (!MEMAREA_OBJECT_IS_ALLOCATED(hdr)) {
+		RTE_MEMAREA_LOG(ERR, "detect invalid object in %s!", ma->init.name);
+		rte_errno = EFAULT;
+		return;
+	}
+	memarea_check_cookie(ma, hdr, 1);
+
+	memarea_lock(ma);
+
+	/** 1st: add to avail list. */
+	TAILQ_INSERT_HEAD(&ma->avail_list, hdr, avail_next);
+	memarea_set_cookie(hdr, 0);
+
+	/** 2nd: merge if previous object is avail. */
+	prev = TAILQ_PREV(hdr, memarea_objhdr_list, obj_next);
+	if (prev != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(prev)) {
+		memarea_check_cookie(ma, prev, 0);
+		memarea_merge_object(ma, prev, hdr);
+		hdr = prev;
+	}
+
+	/** 3rd: merge if next object is avail. */
+	next = TAILQ_NEXT(hdr, obj_next);
+	if (next != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(next)) {
+		memarea_check_cookie(ma, next, 0);
+		memarea_merge_object(ma, hdr, next);
+	}
+
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index e2921ad83b..f11add0bcf 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -130,6 +130,50 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ *
+ * @return
+ *   - NULL on error. Not enough memory, or invalid arguments (see the
+ *     rte_errno).
+ *   - Otherwise, the pointer to the allocated object.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), it must be freed to the same memarea which previous
+ * allocated from. The behaviour of rte_memarea_free() is undefined if the
+ * memarea or pointer does not match these requirements.
+ *
+ * @param ma
+ *   The pointer of memarea. If the ma is NULL, the function does nothing.
+ * @param ptr
+ *   The pointer of memory object which need be freed. If the pointer is NULL,
+ *   the function does nothing.
+ *
+ * @note The rte_errno is set if free failed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..effbd0b488 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,10 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v15 4/6] test/memarea: support alloc and free API test
  2023-07-09 13:00 ` [PATCH v15 " Chengwen Feng
                     ` (2 preceding siblings ...)
  2023-07-09 13:00   ` [PATCH v15 3/6] memarea: support alloc and free API Chengwen Feng
@ 2023-07-09 13:00   ` Chengwen Feng
  2023-07-09 13:00   ` [PATCH v15 5/6] memarea: support dump API Chengwen Feng
  2023-07-09 13:00   ` [PATCH v15 6/6] test/memarea: support dump API test Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-09 13:00 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_alloc() and rte_memarea_free() API
test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 app/test/test_memarea.c | 214 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 213 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 90117caa38..2e13b7431e 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -23,6 +23,12 @@ test_memarea_init_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -78,7 +84,7 @@ test_memarea_create_bad_param(void)
 static int
 test_memarea_create_destroy(void)
 {
-	struct rte_memarea *ma;
+	struct rte_memarea *ma, *src_ma;
 	struct rte_memarea_param init;
 
 	rte_errno = 0;
@@ -98,6 +104,207 @@ test_memarea_create_destroy(void)
 	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with another memarea */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	src_ma = rte_memarea_create(&init);
+	TEST_ASSERT(src_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.ma.src = src_ma;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(src_ma);
+
+	TEST_ASSERT(rte_errno == 0, "Expected ZERO");
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	size_t size;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid ma */
+	ptr = rte_memarea_alloc(NULL, 1);
+	TEST_ASSERT(ptr == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid size (size = 0) */
+	ptr = rte_memarea_alloc(ma, 0);
+	TEST_ASSERT(ptr == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid size (size rewind) */
+	memset(&size, 0xff, sizeof(size));
+	ptr = rte_memarea_alloc(ma, size);
+	TEST_ASSERT(ptr == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	ptr = rte_memarea_alloc(ma, 1);
+	TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, 1);
+
+	/* test for invalid ma */
+	rte_memarea_free(NULL, ptr);
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid ptr */
+	rte_memarea_free(ma, NULL);
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test repeat free */
+	rte_errno = 0;
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	TEST_ASSERT(rte_errno == 0, "Expected Zero");
+	rte_memarea_free(ma, ptr);
+	TEST_ASSERT(rte_errno == EFAULT, "Expected EFAULT");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	void *ptr[ALLOC_MAX_NUM];
+	struct rte_memarea *ma;
+	int i;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	rte_errno = 0;
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++) {
+		ptr[i] = rte_memarea_alloc(ma, 1);
+		TEST_ASSERT(ptr[i] != NULL, "Expected Non-NULL");
+		test_memarea_fill_region(ptr[i], 1);
+	}
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	TEST_ASSERT(rte_errno == 0, "Expected Zero");
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -108,6 +315,11 @@ static struct unit_test_suite memarea_test_suite  = {
 	.unit_test_cases = {
 		TEST_CASE(test_memarea_create_bad_param),
 		TEST_CASE(test_memarea_create_destroy),
+		TEST_CASE(test_memarea_alloc_bad_param),
+		TEST_CASE(test_memarea_free_bad_param),
+		TEST_CASE(test_memarea_alloc_fail),
+		TEST_CASE(test_memarea_free_fail),
+		TEST_CASE(test_memarea_alloc_free),
 
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v15 5/6] memarea: support dump API
  2023-07-09 13:00 ` [PATCH v15 " Chengwen Feng
                     ` (3 preceding siblings ...)
  2023-07-09 13:00   ` [PATCH v15 4/6] test/memarea: support alloc and free API test Chengwen Feng
@ 2023-07-09 13:00   ` Chengwen Feng
  2023-07-09 13:00   ` [PATCH v15 6/6] test/memarea: support dump API test Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-09 13:00 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   3 +
 lib/memarea/rte_memarea.c             | 100 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  21 ++++++
 lib/memarea/version.map               |   1 +
 4 files changed, 125 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 157baf3c7e..ef22294664 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -39,6 +39,9 @@ the memarea.
 The ``rte_memarea_free()`` function is used to free one memory object which
 allocated by ``rte_memarea_alloc()``.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 393068f008..83d31af61c 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -377,3 +377,103 @@ rte_memarea_free(struct rte_memarea *ma, void *ptr)
 
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_MEMAREA)
+		return "memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_algorithm alg)
+{
+	if (alg == RTE_MEMAREA_ALGORITHM_NEXTFIT)
+		return "nextfit";
+	else
+		return "unknown";
+}
+
+static void
+memarea_dump_objects_brief(struct rte_memarea *ma, FILE *f)
+{
+	uint32_t total_objs = 0, total_avail_objs = 0;
+	struct memarea_objhdr *hdr;
+	size_t total_avail_sz = 0;
+
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, 2);
+		total_objs++;
+		if (!MEMAREA_OBJECT_IS_ALLOCATED(hdr)) {
+			total_avail_objs++;
+			total_avail_sz += MEMAREA_OBJECT_GET_SIZE(hdr);
+		}
+	}
+	fprintf(f, "  total-objects: %u\n", total_objs);
+	fprintf(f, "  total-avail-objects: %u\n", total_avail_objs);
+	fprintf(f, "  total-avail-objects-size: 0x%zx\n", total_avail_sz);
+}
+
+static void
+memarea_dump_objects_detail(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_objhdr *hdr;
+	size_t offset;
+	void *ptr;
+
+	fprintf(f, "  objects:\n");
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, 2);
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		offset = RTE_PTR_DIFF(ptr, ma->area_base);
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+		fprintf(f, "    %p off: 0x%zx size: 0x%zx %s\n",
+			ptr, offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#else
+		fprintf(f, "    off: 0x%zx size: 0x%zx %s\n",
+			offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#endif
+	}
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		fprintf(f, "  heap-numa-socket: %d\n", ma->init.heap.socket_id);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		fprintf(f, "  source-memarea: %s\n", ma->init.ma.src->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	fprintf(f, "  area-base: %p\n", ma->area_base);
+	fprintf(f, "  guard-header: %p\n", ma->guard_hdr);
+#endif
+	memarea_dump_objects_brief(ma, f);
+	if (dump_all)
+		memarea_dump_objects_detail(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index f11add0bcf..8092436d57 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -174,6 +174,27 @@ void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
 __rte_experimental
 void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned (the rte_errno is set).
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index effbd0b488..9513d91e0b 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 
 	local: *;
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v15 6/6] test/memarea: support dump API test
  2023-07-09 13:00 ` [PATCH v15 " Chengwen Feng
                     ` (4 preceding siblings ...)
  2023-07-09 13:00   ` [PATCH v15 5/6] memarea: support dump API Chengwen Feng
@ 2023-07-09 13:00   ` Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-09 13:00 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_dump() API test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 app/test/test_memarea.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 2e13b7431e..b373bce309 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -303,6 +303,45 @@ test_memarea_alloc_free(void)
 
 	TEST_ASSERT(rte_errno == 0, "Expected Zero");
 
+	fprintf(stderr, "There should have no allocated object.\n");
+	rte_memarea_dump(ma, stderr, true);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	TEST_ASSERT(ret == -1, "Expected -1");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	TEST_ASSERT(ret == -1, "Expected -1");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	fprintf(stderr, "There should have three allocated object.\n");
+	ret = rte_memarea_dump(ma, stderr, true);
+	TEST_ASSERT(ret == 0, "Expected ZERO");
+
 	rte_memarea_destroy(ma);
 
 	return 0;
@@ -320,6 +359,7 @@ static struct unit_test_suite memarea_test_suite  = {
 		TEST_CASE(test_memarea_alloc_fail),
 		TEST_CASE(test_memarea_free_fail),
 		TEST_CASE(test_memarea_alloc_free),
+		TEST_CASE(test_memarea_dump),
 
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v15 1/6] memarea: introduce memarea library
  2023-07-09 13:00   ` [PATCH v15 1/6] memarea: " Chengwen Feng
@ 2023-07-09 19:46     ` Stephen Hemminger
  0 siblings, 0 replies; 222+ messages in thread
From: Stephen Hemminger @ 2023-07-09 19:46 UTC (permalink / raw)
  To: Chengwen Feng
  Cc: thomas, david.marchand, dev, mb, anatoly.burakov, dmitry.kozliuk,
	jerinjacobk, hofors

On Sun, 9 Jul 2023 13:00:36 +0000
Chengwen Feng <fengchengwen@huawei.com> wrote:

> +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;
> +	};
> +};

FYI - since this data structure has holes in it. Given the current API you can
not assume that those holes are zero and try to reuse them later.

One alternative would to put in explicit reserved fields and check for zero
if you want to add more bits later.

^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v16 0/6] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (19 preceding siblings ...)
  2023-07-09 13:00 ` [PATCH v15 " Chengwen Feng
@ 2023-07-10  6:49 ` Chengwen Feng
  2023-07-10  6:49   ` [PATCH v16 1/6] memarea: " Chengwen Feng
                     ` (5 more replies)
  2023-07-18  2:40 ` [PATCH v17 0/6] introduce memarea library Chengwen Feng
                   ` (2 subsequent siblings)
  23 siblings, 6 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-10  6:49 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- 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 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.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Chengwen Feng (6):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc and free API
  test/memarea: support alloc and free API test
  memarea: support dump API
  test/memarea: support dump API test

---
v16:
* fix compile failed when clang with thread-safety-analysis
  (by disable annotate_locks in lib/memarea/meson.build).
* add reserved field for 'struct rte_memarea_param' which address
  Stephen's comments.
* fix typo.
v15:
* rebase 23.07
* address Anatoly's mostly comments: use rte_errno, abstract cookie
  helper, more comment about critical function, rename add with split,
  doc limitation.
v14:
* address Stephen's comments: RTE_MEMAREA_LOG use easy impl and add
  __func__ print.
v13:
* address Morten's comments: make debug cookies optional, controlled
  by RTE_LIBRTE_MEMAREA_DEBUG; disabled by default.
* reduce management data overhead.
v12:
* remove rte_memarea_refcnt_update() API which address Dongdong's
  comments.
* refine the variable naming.
* fix some bugs.
v11:
* rebase 23.03
* remove "app/test: add memarea to malloc-perf-autotest" because the
  two algorithm are not comparable which also address previous
  comments.
v10:
* support windows platform.
* add rte_memarea.libc perftest to malloc-perf-autotest.
v9:
* address Dmitry's comments.
* drop features of SOURCE_USER and backup memarea mechanism.
* rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
  to keep with rte_mbuf_refcnt_update name style.
* fix memarea perftest compile failed at windows platform.
* fix spell warning.
v8:
* address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
* small feature patches are combined.
* enhanced backup memory mechanism.
* add memarea to malloc-perf-autotest.
* other tiny naming optimize.
v7:
* repost patches as there are spread over different series in patchwork.
v6:
* address Mattias's comments.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 391 ++++++++++++++++++++
 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  |  57 +++
 doc/guides/rel_notes/release_23_07.rst |   6 +
 lib/memarea/memarea_private.h          |  96 +++++
 lib/memarea/meson.build                |  12 +
 lib/memarea/rte_memarea.c              | 485 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 206 +++++++++++
 lib/memarea/version.map                |  15 +
 lib/meson.build                        |   1 +
 14 files changed, 1281 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v16 1/6] memarea: introduce memarea library
  2023-07-10  6:49 ` [PATCH v16 0/6] introduce memarea library Chengwen Feng
@ 2023-07-10  6:49   ` Chengwen Feng
  2023-07-17 13:19     ` Burakov, Anatoly
  2023-07-10  6:49   ` [PATCH v16 2/6] test/memarea: support memarea test Chengwen Feng
                     ` (4 subsequent siblings)
  5 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2023-07-10  6:49 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

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  |  48 ++++++
 doc/guides/rel_notes/release_23_07.rst |   6 +
 lib/memarea/memarea_private.h          |  86 ++++++++++
 lib/memarea/meson.build                |  12 ++
 lib/memarea/rte_memarea.c              | 221 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 141 ++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 12 files changed, 536 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 <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>
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..384c6dde9d
--- /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 <rte_memarea.h>
+
+#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_list. */
+	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..48499626fd
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 HiSilicon Limited
+
+sources = files(
+        'rte_memarea.c',
+)
+headers = files(
+        'rte_memarea.h',
+)
+deps += []
+
+annotate_locks = false
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
new file mode 100644
index 0000000000..69ffeac4e4
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <rte_common.h>
+#include <rte_errno.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)
+{
+	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;
+	}
+
+	if (init->reserved_bits != 0 || init->reserved_64s[0] != 0 ||
+	    init->reserved_64s[1] != 0) {
+		RTE_MEMAREA_LOG(ERR, "%s reserved field not zero!", init->name);
+		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..1d4381efd7
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,141 @@
+/* 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 <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_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;
+	/** Reserved for future field, should be initialized to zero. */
+	uint32_t reserved_bits : 31;
+	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;
+	};
+	/** Reserved for future fields, should be initialized to zero. */
+	uint64_t reserved_64s[2];
+};
+
+/**
+ * @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


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v16 2/6] test/memarea: support memarea test
  2023-07-10  6:49 ` [PATCH v16 0/6] introduce memarea library Chengwen Feng
  2023-07-10  6:49   ` [PATCH v16 1/6] memarea: " Chengwen Feng
@ 2023-07-10  6:49   ` Chengwen Feng
  2023-07-17 13:22     ` Burakov, Anatoly
  2023-07-10  6:49   ` [PATCH v16 3/6] memarea: support alloc and free API Chengwen Feng
                     ` (3 subsequent siblings)
  5 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2023-07-10  6:49 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports memarea test of 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             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 139 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 142 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 93de0876f1..cc082ae821 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1616,6 +1616,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index 3e0a2360a3..5e3012328d 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -83,6 +83,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -200,6 +201,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..d8d5fc35d4
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_errno.h>
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#include "test.h"
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+static void
+test_memarea_init_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "autotest");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid source */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for total_sz */
+	test_memarea_init_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for memarea NULL */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for algorithm invalid */
+	test_memarea_init_param(&init);
+	init.alg = RTE_MEMAREA_ALGORITHM_NEXTFIT + 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for reserved field */
+	test_memarea_init_param(&init);
+	init.reserved_bits = 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	test_memarea_init_param(&init);
+	init.reserved_64s[0] = 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	test_memarea_init_param(&init);
+	init.reserved_64s[1] = 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	return 0;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	struct rte_memarea *ma;
+	struct rte_memarea_param init;
+
+	rte_errno = 0;
+
+	/* test for create with HEAP */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.heap.socket_id = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static struct unit_test_suite memarea_test_suite  = {
+	.suite_name = "Memarea Unit Test Suite",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+		TEST_CASE(test_memarea_create_bad_param),
+		TEST_CASE(test_memarea_create_destroy),
+
+		TEST_CASES_END() /**< NULL terminate unit test array */
+	}
+};
+
+static int
+test_memarea(void)
+{
+	return unit_test_suite_runner(&memarea_test_suite);
+}
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v16 3/6] memarea: support alloc and free API
  2023-07-10  6:49 ` [PATCH v16 0/6] introduce memarea library Chengwen Feng
  2023-07-10  6:49   ` [PATCH v16 1/6] memarea: " Chengwen Feng
  2023-07-10  6:49   ` [PATCH v16 2/6] test/memarea: support memarea test Chengwen Feng
@ 2023-07-10  6:49   ` Chengwen Feng
  2023-07-17 13:33     ` Burakov, Anatoly
  2023-07-10  6:49   ` [PATCH v16 4/6] test/memarea: support alloc and free API test Chengwen Feng
                     ` (2 subsequent siblings)
  5 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2023-07-10  6:49 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_alloc() and rte_memarea_free() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   6 +
 lib/memarea/memarea_private.h         |  10 ++
 lib/memarea/rte_memarea.c             | 164 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  44 +++++++
 lib/memarea/version.map               |   2 +
 5 files changed, 226 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index bf19090294..157baf3c7e 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,12 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index 384c6dde9d..cef7d0f859 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -22,10 +22,20 @@
 #define MEMAREA_OBJECT_GET_SIZE(hdr) \
 		((uintptr_t)TAILQ_NEXT((hdr), obj_next) - (uintptr_t)(hdr) - \
 		 sizeof(struct memarea_objhdr) - sizeof(struct memarea_objtlr))
+#define MEMAREA_SPLIT_OBJECT_MIN_SIZE \
+		(sizeof(struct memarea_objhdr) + MEMAREA_OBJECT_SIZE_ALIGN + \
+		 sizeof(struct memarea_objtlr))
+#define MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz) \
+		RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz + \
+			    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))
+#define MEMAREA_SPLIT_OBJECT_MIN_SIZE \
+		(sizeof(struct memarea_objhdr) + MEMAREA_OBJECT_SIZE_ALIGN)
+#define MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz) \
+		RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz)
 #endif
 
 struct memarea_objhdr {
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 69ffeac4e4..d941e64a1e 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,8 +2,10 @@
  * Copyright(c) 2023 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_errno.h>
@@ -94,6 +96,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 					init->heap.socket_id);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		ptr = memarea_alloc_from_libc(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		ptr = rte_memarea_alloc(init->ma.src, init->total_sz);
 
 	return ptr;
 }
@@ -105,6 +109,8 @@ memarea_free_area(const struct rte_memarea_param *init, void *ptr)
 		rte_free(ptr);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ptr);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		rte_memarea_free(init->ma.src, ptr);
 }
 
 /**
@@ -219,3 +225,161 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(&ma->init, ma->area_base);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+/**
+ * Check cookie or panic.
+ *
+ * @param status
+ *   - 0: object is supposed to be available.
+ *   - 1: object is supposed to be allocated.
+ *   - 2: just check that cookie is valid (available or allocated).
+ */
+static inline void
+memarea_check_cookie(const struct rte_memarea *ma, const struct memarea_objhdr *hdr, int status)
+{
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	static const char *const str[] = { "PASS", "FAILED" };
+	struct memarea_objtlr *tlr;
+	bool hdr_fail, tlr_fail;
+
+	if (hdr == ma->guard_hdr)
+		return;
+
+	tlr = RTE_PTR_SUB(TAILQ_NEXT(hdr, obj_next), sizeof(struct memarea_objtlr));
+	hdr_fail = (status == 0 && hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE) ||
+		   (status == 1 && hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE) ||
+		   (status == 2 && (hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE &&
+				    hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE));
+	tlr_fail = (tlr->cookie != MEMAREA_OBJECT_TRAILER_COOKIE);
+	if (!hdr_fail && !tlr_fail)
+		return;
+
+	rte_panic("MEMAREA: %s check cookies failed! addr-%p header-cookie<0x%" PRIx64 " %s> trailer-cookie<0x%" PRIx64 " %s>\n",
+		ma->init.name, RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr)),
+		hdr->cookie, str[hdr_fail], tlr->cookie, str[tlr_fail]);
+#else
+	RTE_SET_USED(ma);
+	RTE_SET_USED(hdr);
+	RTE_SET_USED(status);
+#endif
+}
+
+static inline void
+memarea_split_object(struct rte_memarea *ma, struct memarea_objhdr *hdr, size_t alloc_sz)
+{
+	struct memarea_objhdr *split_hdr;
+
+	split_hdr = MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz);
+	memarea_set_cookie(split_hdr, 2);
+	TAILQ_INSERT_AFTER(&ma->obj_list, hdr, split_hdr, obj_next);
+	TAILQ_INSERT_AFTER(&ma->avail_list, hdr, split_hdr, avail_next);
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size)
+{
+	size_t align_sz = RTE_ALIGN(size, MEMAREA_OBJECT_SIZE_ALIGN);
+	struct memarea_objhdr *hdr;
+	size_t avail_sz;
+	void *ptr = NULL;
+
+	if (ma == NULL || size == 0 || align_sz < size) {
+		rte_errno = EINVAL;
+		return ptr;
+	}
+
+	memarea_lock(ma);
+
+	/** traverse every available object, return the first satisfied one. */
+	TAILQ_FOREACH(hdr, &ma->avail_list, avail_next) {
+		/** 1st: check whether the object size meets. */
+		memarea_check_cookie(ma, hdr, 0);
+		avail_sz = MEMAREA_OBJECT_GET_SIZE(hdr);
+		if (avail_sz < align_sz)
+			continue;
+
+		/** 2nd: if the object size is too long, a new object can be split. */
+		if (avail_sz - align_sz > MEMAREA_SPLIT_OBJECT_MIN_SIZE)
+			memarea_split_object(ma, hdr, align_sz);
+
+		/** 3rd: allocate successful. */
+		TAILQ_REMOVE(&ma->avail_list, hdr, avail_next);
+		MEMAREA_OBJECT_MARK_ALLOCATED(hdr);
+		memarea_set_cookie(hdr, 1);
+
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		break;
+	}
+
+	memarea_unlock(ma);
+
+	if (ptr == NULL)
+		rte_errno = ENOMEM;
+	return ptr;
+}
+
+static inline void
+memarea_merge_object(struct rte_memarea *ma, struct memarea_objhdr *curr,
+		   struct memarea_objhdr *next)
+{
+	RTE_SET_USED(curr);
+	TAILQ_REMOVE(&ma->obj_list, next, obj_next);
+	TAILQ_REMOVE(&ma->avail_list, next, avail_next);
+	memarea_set_cookie(next, 4);
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	struct memarea_objhdr *hdr, *prev, *next;
+
+	if (ma == NULL || ptr == NULL) {
+		rte_errno = EINVAL;
+		return;
+	}
+
+	hdr = RTE_PTR_SUB(ptr, sizeof(struct memarea_objhdr));
+	if (!MEMAREA_OBJECT_IS_ALLOCATED(hdr)) {
+		RTE_MEMAREA_LOG(ERR, "detect invalid object in %s!", ma->init.name);
+		rte_errno = EFAULT;
+		return;
+	}
+	memarea_check_cookie(ma, hdr, 1);
+
+	memarea_lock(ma);
+
+	/** 1st: add to avail list. */
+	TAILQ_INSERT_HEAD(&ma->avail_list, hdr, avail_next);
+	memarea_set_cookie(hdr, 0);
+
+	/** 2nd: merge if previous object is avail. */
+	prev = TAILQ_PREV(hdr, memarea_objhdr_list, obj_next);
+	if (prev != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(prev)) {
+		memarea_check_cookie(ma, prev, 0);
+		memarea_merge_object(ma, prev, hdr);
+		hdr = prev;
+	}
+
+	/** 3rd: merge if next object is avail. */
+	next = TAILQ_NEXT(hdr, obj_next);
+	if (next != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(next)) {
+		memarea_check_cookie(ma, next, 0);
+		memarea_merge_object(ma, hdr, next);
+	}
+
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 1d4381efd7..f771fcaf68 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -134,6 +134,50 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ *
+ * @return
+ *   - NULL on error. Not enough memory, or invalid arguments (see the
+ *     rte_errno).
+ *   - Otherwise, the pointer to the allocated object.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), it must be freed to the same memarea which previous
+ * allocated from. The behaviour of rte_memarea_free() is undefined if the
+ * memarea or pointer does not match these requirements.
+ *
+ * @param ma
+ *   The pointer of memarea. If the ma is NULL, the function does nothing.
+ * @param ptr
+ *   The pointer of memory object which need be freed. If the pointer is NULL,
+ *   the function does nothing.
+ *
+ * @note The rte_errno is set if free failed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..effbd0b488 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,10 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v16 4/6] test/memarea: support alloc and free API test
  2023-07-10  6:49 ` [PATCH v16 0/6] introduce memarea library Chengwen Feng
                     ` (2 preceding siblings ...)
  2023-07-10  6:49   ` [PATCH v16 3/6] memarea: support alloc and free API Chengwen Feng
@ 2023-07-10  6:49   ` Chengwen Feng
  2023-07-17 13:57     ` Burakov, Anatoly
  2023-07-10  6:49   ` [PATCH v16 5/6] memarea: support dump API Chengwen Feng
  2023-07-10  6:49   ` [PATCH v16 6/6] test/memarea: support dump API test Chengwen Feng
  5 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2023-07-10  6:49 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_alloc() and rte_memarea_free() API
test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 app/test/test_memarea.c | 214 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 213 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index d8d5fc35d4..4053cdcac9 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -23,6 +23,12 @@ test_memarea_init_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -95,7 +101,7 @@ test_memarea_create_bad_param(void)
 static int
 test_memarea_create_destroy(void)
 {
-	struct rte_memarea *ma;
+	struct rte_memarea *ma, *src_ma;
 	struct rte_memarea_param init;
 
 	rte_errno = 0;
@@ -115,6 +121,207 @@ test_memarea_create_destroy(void)
 	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with another memarea */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	src_ma = rte_memarea_create(&init);
+	TEST_ASSERT(src_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.ma.src = src_ma;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(src_ma);
+
+	TEST_ASSERT(rte_errno == 0, "Expected ZERO");
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	size_t size;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid ma */
+	ptr = rte_memarea_alloc(NULL, 1);
+	TEST_ASSERT(ptr == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid size (size = 0) */
+	ptr = rte_memarea_alloc(ma, 0);
+	TEST_ASSERT(ptr == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid size (size rewind) */
+	memset(&size, 0xff, sizeof(size));
+	ptr = rte_memarea_alloc(ma, size);
+	TEST_ASSERT(ptr == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	ptr = rte_memarea_alloc(ma, 1);
+	TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, 1);
+
+	/* test for invalid ma */
+	rte_memarea_free(NULL, ptr);
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid ptr */
+	rte_memarea_free(ma, NULL);
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test repeat free */
+	rte_errno = 0;
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	TEST_ASSERT(rte_errno == 0, "Expected Zero");
+	rte_memarea_free(ma, ptr);
+	TEST_ASSERT(rte_errno == EFAULT, "Expected EFAULT");
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	void *ptr[ALLOC_MAX_NUM];
+	struct rte_memarea *ma;
+	int i;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	rte_errno = 0;
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++) {
+		ptr[i] = rte_memarea_alloc(ma, 1);
+		TEST_ASSERT(ptr[i] != NULL, "Expected Non-NULL");
+		test_memarea_fill_region(ptr[i], 1);
+	}
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	TEST_ASSERT(rte_errno == 0, "Expected Zero");
+
+	rte_memarea_destroy(ma);
+
 	return 0;
 }
 
@@ -125,6 +332,11 @@ static struct unit_test_suite memarea_test_suite  = {
 	.unit_test_cases = {
 		TEST_CASE(test_memarea_create_bad_param),
 		TEST_CASE(test_memarea_create_destroy),
+		TEST_CASE(test_memarea_alloc_bad_param),
+		TEST_CASE(test_memarea_free_bad_param),
+		TEST_CASE(test_memarea_alloc_fail),
+		TEST_CASE(test_memarea_free_fail),
+		TEST_CASE(test_memarea_alloc_free),
 
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v16 5/6] memarea: support dump API
  2023-07-10  6:49 ` [PATCH v16 0/6] introduce memarea library Chengwen Feng
                     ` (3 preceding siblings ...)
  2023-07-10  6:49   ` [PATCH v16 4/6] test/memarea: support alloc and free API test Chengwen Feng
@ 2023-07-10  6:49   ` Chengwen Feng
  2023-07-10  6:49   ` [PATCH v16 6/6] test/memarea: support dump API test Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-10  6:49 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   3 +
 lib/memarea/rte_memarea.c             | 100 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  21 ++++++
 lib/memarea/version.map               |   1 +
 4 files changed, 125 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 157baf3c7e..ef22294664 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -39,6 +39,9 @@ the memarea.
 The ``rte_memarea_free()`` function is used to free one memory object which
 allocated by ``rte_memarea_alloc()``.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index d941e64a1e..4a2cc84d84 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -383,3 +383,103 @@ rte_memarea_free(struct rte_memarea *ma, void *ptr)
 
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_MEMAREA)
+		return "memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_algorithm alg)
+{
+	if (alg == RTE_MEMAREA_ALGORITHM_NEXTFIT)
+		return "nextfit";
+	else
+		return "unknown";
+}
+
+static void
+memarea_dump_objects_brief(struct rte_memarea *ma, FILE *f)
+{
+	uint32_t total_objs = 0, total_avail_objs = 0;
+	struct memarea_objhdr *hdr;
+	size_t total_avail_sz = 0;
+
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, 2);
+		total_objs++;
+		if (!MEMAREA_OBJECT_IS_ALLOCATED(hdr)) {
+			total_avail_objs++;
+			total_avail_sz += MEMAREA_OBJECT_GET_SIZE(hdr);
+		}
+	}
+	fprintf(f, "  total-objects: %u\n", total_objs);
+	fprintf(f, "  total-avail-objects: %u\n", total_avail_objs);
+	fprintf(f, "  total-avail-objects-size: 0x%zx\n", total_avail_sz);
+}
+
+static void
+memarea_dump_objects_detail(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_objhdr *hdr;
+	size_t offset;
+	void *ptr;
+
+	fprintf(f, "  objects:\n");
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, 2);
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		offset = RTE_PTR_DIFF(ptr, ma->area_base);
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+		fprintf(f, "    %p off: 0x%zx size: 0x%zx %s\n",
+			ptr, offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#else
+		fprintf(f, "    off: 0x%zx size: 0x%zx %s\n",
+			offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#endif
+	}
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		fprintf(f, "  heap-numa-socket: %d\n", ma->init.heap.socket_id);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		fprintf(f, "  source-memarea: %s\n", ma->init.ma.src->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	fprintf(f, "  area-base: %p\n", ma->area_base);
+	fprintf(f, "  guard-header: %p\n", ma->guard_hdr);
+#endif
+	memarea_dump_objects_brief(ma, f);
+	if (dump_all)
+		memarea_dump_objects_detail(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index f771fcaf68..834fddcc4d 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -178,6 +178,27 @@ void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
 __rte_experimental
 void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned (the rte_errno is set).
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index effbd0b488..9513d91e0b 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 
 	local: *;
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v16 6/6] test/memarea: support dump API test
  2023-07-10  6:49 ` [PATCH v16 0/6] introduce memarea library Chengwen Feng
                     ` (4 preceding siblings ...)
  2023-07-10  6:49   ` [PATCH v16 5/6] memarea: support dump API Chengwen Feng
@ 2023-07-10  6:49   ` Chengwen Feng
  2023-07-19 12:09     ` Burakov, Anatoly
  5 siblings, 1 reply; 222+ messages in thread
From: Chengwen Feng @ 2023-07-10  6:49 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_dump() API test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 app/test/test_memarea.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 4053cdcac9..6511a86699 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -320,6 +320,45 @@ test_memarea_alloc_free(void)
 
 	TEST_ASSERT(rte_errno == 0, "Expected Zero");
 
+	fprintf(stderr, "There should have no allocated object.\n");
+	rte_memarea_dump(ma, stderr, true);
+
+	rte_memarea_destroy(ma);
+
+	return 0;
+}
+
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	TEST_ASSERT(ret == -1, "Expected -1");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	TEST_ASSERT(ret == -1, "Expected -1");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	fprintf(stderr, "There should have three allocated object.\n");
+	ret = rte_memarea_dump(ma, stderr, true);
+	TEST_ASSERT(ret == 0, "Expected ZERO");
+
 	rte_memarea_destroy(ma);
 
 	return 0;
@@ -337,6 +376,7 @@ static struct unit_test_suite memarea_test_suite  = {
 		TEST_CASE(test_memarea_alloc_fail),
 		TEST_CASE(test_memarea_free_fail),
 		TEST_CASE(test_memarea_alloc_free),
+		TEST_CASE(test_memarea_dump),
 
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v16 1/6] memarea: introduce memarea library
  2023-07-10  6:49   ` [PATCH v16 1/6] memarea: " Chengwen Feng
@ 2023-07-17 13:19     ` Burakov, Anatoly
  0 siblings, 0 replies; 222+ messages in thread
From: Burakov, Anatoly @ 2023-07-17 13:19 UTC (permalink / raw)
  To: Chengwen Feng, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

On 7/10/2023 7:49 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>
> ---

Hi,

Couple of nitpicks below.

> +	/** 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.

Trailer* cookie

> +
> +/**
> + * 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.

I feel like it would've been better if all of these constants are 
defined, so that it wouldn't be '0' but rather STATUS_AVAILABLE (or 
similar).

Otherwise, great bit of rework, so thanks for the effort!

Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>

-- 
Thanks,
Anatoly


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v16 2/6] test/memarea: support memarea test
  2023-07-10  6:49   ` [PATCH v16 2/6] test/memarea: support memarea test Chengwen Feng
@ 2023-07-17 13:22     ` Burakov, Anatoly
  0 siblings, 0 replies; 222+ messages in thread
From: Burakov, Anatoly @ 2023-07-17 13:22 UTC (permalink / raw)
  To: Chengwen Feng, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

On 7/10/2023 7:49 AM, Chengwen Feng wrote:
> This patch supports memarea test of 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>
> ---

Hi,

> +	/* test for create with LIBC */
> +	test_memarea_init_param(&init);
> +	init.source = RTE_MEMAREA_SOURCE_LIBC;
> +	ma = rte_memarea_create(&init);
> +	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
> +	rte_memarea_destroy(ma);
> +
> +	return 0;

Here and in other test functions: I think TEST_SUCCESS would be the 
semantically correct value to return.

> +}
> +
> +static struct unit_test_suite memarea_test_suite  = {
> +	.suite_name = "Memarea Unit Test Suite",
> +	.setup = NULL,
> +	.teardown = NULL,
> +	.unit_test_cases = {
> +		TEST_CASE(test_memarea_create_bad_param),
> +		TEST_CASE(test_memarea_create_destroy),
> +
> +		TEST_CASES_END() /**< NULL terminate unit test array */
> +	}
> +};
> +
> +static int
> +test_memarea(void)
> +{
> +	return unit_test_suite_runner(&memarea_test_suite);
> +}
> +
> +REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);

Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>

-- 
Thanks,
Anatoly


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v16 3/6] memarea: support alloc and free API
  2023-07-10  6:49   ` [PATCH v16 3/6] memarea: support alloc and free API Chengwen Feng
@ 2023-07-17 13:33     ` Burakov, Anatoly
  0 siblings, 0 replies; 222+ messages in thread
From: Burakov, Anatoly @ 2023-07-17 13:33 UTC (permalink / raw)
  To: Chengwen Feng, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

On 7/10/2023 7:49 AM, Chengwen Feng wrote:
> This patch supports rte_memarea_alloc() and rte_memarea_free() API.
> 
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
> Acked-by: Morten Brørup <mb@smartsharesystems.com>
> ---

Hi,

Kind of a general question: the allocation code doesn't seem to take 
into account that since we're potentially reusing memory between 
alloc/free calls, the memory may/will be dirty, and any subsequent 
alloc/free call may require memset(0) from the user. This is the kind of 
thing C programmers are familiar with already, so it's not a big deal, 
I'm just wondering what your intention was with this API: do you think 
it should be user's responsibility to erase memory before using it, or 
can we make it part of API guarantees? If it's the latter, then it would 
be good to have some sort of note in documentation explicitly mentioning 
that the memory returned is not guaranteed to be zeroed out?

> +static inline void
> +memarea_unlock(struct rte_memarea *ma)
> +{
> +	if (ma->init.mt_safe)
> +		rte_spinlock_unlock(&ma->lock);
> +}
> +
> +/**
> + * Check cookie or panic.
> + *
> + * @param status
> + *   - 0: object is supposed to be available.
> + *   - 1: object is supposed to be allocated.
> + *   - 2: just check that cookie is valid (available or allocated).
> + */

Same as with previous patches: I feel like it would've been better if 
status were #define'd somewhere, so that we don't use explicit numbers 
in code.

Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>

-- 
Thanks,
Anatoly


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v16 4/6] test/memarea: support alloc and free API test
  2023-07-10  6:49   ` [PATCH v16 4/6] test/memarea: support alloc and free API test Chengwen Feng
@ 2023-07-17 13:57     ` Burakov, Anatoly
  2023-07-18  2:49       ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: Burakov, Anatoly @ 2023-07-17 13:57 UTC (permalink / raw)
  To: Chengwen Feng, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

On 7/10/2023 7:49 AM, Chengwen Feng wrote:
> This patch supports rte_memarea_alloc() and rte_memarea_free() API
> test.
> 
> 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
> +test_memarea_free_fail(void)
> +{
> +	struct rte_memarea_param init;
> +	struct rte_memarea *ma;
> +	void *ptr;
> +
> +	test_memarea_init_param(&init);
> +	init.source = RTE_MEMAREA_SOURCE_LIBC;
> +	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
> +	ma = rte_memarea_create(&init);
> +	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
> +
> +	/* test repeat free */
> +	rte_errno = 0;
> +	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
> +	TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
> +	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
> +	rte_memarea_free(ma, ptr);
> +	TEST_ASSERT(rte_errno == 0, "Expected Zero");
> +	rte_memarea_free(ma, ptr);
> +	TEST_ASSERT(rte_errno == EFAULT, "Expected EFAULT");
> +
> +	rte_memarea_destroy(ma);
> +
> +	return 0;

Same as in other, I think TEST_SUCCESS would be more correct (even 
though DPDK codebase doesn't always follow that convention).

Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
-- 
Thanks,
Anatoly


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v17 0/6] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (20 preceding siblings ...)
  2023-07-10  6:49 ` [PATCH v16 0/6] introduce memarea library Chengwen Feng
@ 2023-07-18  2:40 ` Chengwen Feng
  2023-07-18  2:40   ` [PATCH v17 1/6] memarea: " Chengwen Feng
                     ` (5 more replies)
  2023-07-18 13:46 ` [PATCH v18 0/6] introduce memarea library Chengwen Feng
  2023-07-20  9:22 ` [PATCH v19 0/6] introduce memarea library Chengwen Feng
  23 siblings, 6 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18  2:40 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- 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 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.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Chengwen Feng (6):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc and free API
  test/memarea: support alloc and free API test
  memarea: support dump API
  test/memarea: support dump API test

---
v17:
* address Anatoly's comments: add note and enum for cookie and typo.
v16:
* fix compile failed when clang with thread-safety-analysis
  (by disable annotate_locks in lib/memarea/meson.build).
* add reserved field for 'struct rte_memarea_param' which address
  Stephen's comments.
* fix typo.
v15:
* rebase 23.07
* address Anatoly's mostly comments: use rte_errno, abstract cookie
  helper, more comment about critical function, rename add with split,
  doc limitation.
v14:
* address Stephen's comments: RTE_MEMAREA_LOG use easy impl and add
  __func__ print.
v13:
* address Morten's comments: make debug cookies optional, controlled
  by RTE_LIBRTE_MEMAREA_DEBUG; disabled by default.
* reduce management data overhead.
v12:
* remove rte_memarea_refcnt_update() API which address Dongdong's
  comments.
* refine the variable naming.
* fix some bugs.
v11:
* rebase 23.03
* remove "app/test: add memarea to malloc-perf-autotest" because the
  two algorithm are not comparable which also address previous
  comments.
v10:
* support windows platform.
* add rte_memarea.libc perftest to malloc-perf-autotest.
v9:
* address Dmitry's comments.
* drop features of SOURCE_USER and backup memarea mechanism.
* rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
  to keep with rte_mbuf_refcnt_update name style.
* fix memarea perftest compile failed at windows platform.
* fix spell warning.
v8:
* address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
* small feature patches are combined.
* enhanced backup memory mechanism.
* add memarea to malloc-perf-autotest.
* other tiny naming optimize.
v7:
* repost patches as there are spread over different series in patchwork.
v6:
* address Mattias's comments.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 391 +++++++++++++++++++++
 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  |  57 +++
 doc/guides/rel_notes/release_23_07.rst |   6 +
 lib/memarea/memarea_private.h          | 126 +++++++
 lib/memarea/meson.build                |  12 +
 lib/memarea/rte_memarea.c              | 467 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 208 +++++++++++
 lib/memarea/version.map                |  15 +
 lib/meson.build                        |   1 +
 14 files changed, 1295 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v17 1/6] memarea: introduce memarea library
  2023-07-18  2:40 ` [PATCH v17 0/6] introduce memarea library Chengwen Feng
@ 2023-07-18  2:40   ` Chengwen Feng
  2023-07-18  2:40   ` [PATCH v17 2/6] test/memarea: support memarea test Chengwen Feng
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18  2:40 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

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>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.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  |  48 ++++++
 doc/guides/rel_notes/release_23_07.rst |   6 +
 lib/memarea/memarea_private.h          | 116 ++++++++++++++
 lib/memarea/meson.build                |  12 ++
 lib/memarea/rte_memarea.c              | 208 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 141 +++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 12 files changed, 553 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 5bb8090ebe..9ca665219b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1613,6 +1613,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>
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 d4af85b3ff..4034a9e9c0 100644
--- a/doc/guides/rel_notes/release_23_07.rst
+++ b/doc/guides/rel_notes/release_23_07.rst
@@ -222,6 +222,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..fd485bb7e7
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,116 @@
+/* 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_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
+
+/** Object cookie target status. */
+enum {
+	/** Object is set to be available, but don't set trailer cookie. */
+	COOKIE_TARGET_STATUS_AVAILABLE,
+	/** Object is set to be allocated, but don't set trailer cookie. */
+	COOKIE_TARGET_STATUS_ALLOCATED,
+	/** object is new split, the header cookie will set to be available,
+	 * the trailer cookie of the previous object will be set.
+	 */
+	COOKIE_TARGET_STATUS_NEW_AVAILABLE,
+	/** object is new split, the header cookie will set to be allocated,
+	 * the trailer cookie of the previous object will be set.
+	 */
+	COOKIE_TARGET_STATUS_NEW_ALLOCATED,
+	/** Object is to be merged, it will no longer exist. the header cookie
+	 * is cleared and the trailer cookie of the previous object is cleared.
+	 */
+	COOKIE_TARGET_STATUS_CLEARED,
+};
+
+/** Object cookie expect status. */
+enum {
+	/** Object is supposed to be available. */
+	COOKIE_EXPECT_STATUS_AVAILABLE,
+	/** Object is supposed to be allocated. */
+	COOKIE_EXPECT_STATUS_ALLOCATED,
+	/** Object is supposed to be valid (available or allocated). */
+	COOKIE_EXPECT_STATUS_VALID,
+};
+
+#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_list. */
+	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 trailer 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..48499626fd
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 HiSilicon Limited
+
+sources = files(
+        'rte_memarea.c',
+)
+headers = files(
+        'rte_memarea.h',
+)
+deps += []
+
+annotate_locks = false
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
new file mode 100644
index 0000000000..ea9067fb35
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,208 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <rte_common.h>
+#include <rte_errno.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)
+{
+	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;
+	}
+
+	if (init->reserved_bits != 0 || init->reserved_64s[0] != 0 ||
+	    init->reserved_64s[1] != 0) {
+		RTE_MEMAREA_LOG(ERR, "%s reserved field not zero!", init->name);
+		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);
+}
+
+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, COOKIE_TARGET_STATUS_AVAILABLE);
+
+	/** 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, COOKIE_TARGET_STATUS_NEW_ALLOCATED);
+
+	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..1d4381efd7
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,141 @@
+/* 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 <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_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;
+	/** Reserved for future field, should be initialized to zero. */
+	uint32_t reserved_bits : 31;
+	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;
+	};
+	/** Reserved for future fields, should be initialized to zero. */
+	uint64_t reserved_64s[2];
+};
+
+/**
+ * @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


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v17 2/6] test/memarea: support memarea test
  2023-07-18  2:40 ` [PATCH v17 0/6] introduce memarea library Chengwen Feng
  2023-07-18  2:40   ` [PATCH v17 1/6] memarea: " Chengwen Feng
@ 2023-07-18  2:40   ` Chengwen Feng
  2023-07-18  2:40   ` [PATCH v17 3/6] memarea: support alloc and free API Chengwen Feng
                     ` (3 subsequent siblings)
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18  2:40 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports memarea test of 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>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 139 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 142 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 9ca665219b..b9b9ed2932 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1617,6 +1617,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index b89cf0368f..0d9701d8c6 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -83,6 +83,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -201,6 +202,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..a2191eb0eb
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_errno.h>
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#include "test.h"
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+static void
+test_memarea_init_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "autotest");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid source */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for total_sz */
+	test_memarea_init_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for memarea NULL */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for algorithm invalid */
+	test_memarea_init_param(&init);
+	init.alg = RTE_MEMAREA_ALGORITHM_NEXTFIT + 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for reserved field */
+	test_memarea_init_param(&init);
+	init.reserved_bits = 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	test_memarea_init_param(&init);
+	init.reserved_64s[0] = 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	test_memarea_init_param(&init);
+	init.reserved_64s[1] = 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	struct rte_memarea *ma;
+	struct rte_memarea_param init;
+
+	rte_errno = 0;
+
+	/* test for create with HEAP */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.heap.socket_id = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite memarea_test_suite  = {
+	.suite_name = "Memarea Unit Test Suite",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+		TEST_CASE(test_memarea_create_bad_param),
+		TEST_CASE(test_memarea_create_destroy),
+
+		TEST_CASES_END() /**< NULL terminate unit test array */
+	}
+};
+
+static int
+test_memarea(void)
+{
+	return unit_test_suite_runner(&memarea_test_suite);
+}
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v17 3/6] memarea: support alloc and free API
  2023-07-18  2:40 ` [PATCH v17 0/6] introduce memarea library Chengwen Feng
  2023-07-18  2:40   ` [PATCH v17 1/6] memarea: " Chengwen Feng
  2023-07-18  2:40   ` [PATCH v17 2/6] test/memarea: support memarea test Chengwen Feng
@ 2023-07-18  2:40   ` Chengwen Feng
  2023-07-18  2:40   ` [PATCH v17 4/6] test/memarea: support alloc and free API test Chengwen Feng
                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18  2:40 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_alloc() and rte_memarea_free() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   6 +
 lib/memarea/memarea_private.h         |  10 ++
 lib/memarea/rte_memarea.c             | 159 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  46 ++++++++
 lib/memarea/version.map               |   2 +
 5 files changed, 223 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index bf19090294..157baf3c7e 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,12 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index fd485bb7e7..ab6253294e 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -52,10 +52,20 @@ enum {
 #define MEMAREA_OBJECT_GET_SIZE(hdr) \
 		((uintptr_t)TAILQ_NEXT((hdr), obj_next) - (uintptr_t)(hdr) - \
 		 sizeof(struct memarea_objhdr) - sizeof(struct memarea_objtlr))
+#define MEMAREA_SPLIT_OBJECT_MIN_SIZE \
+		(sizeof(struct memarea_objhdr) + MEMAREA_OBJECT_SIZE_ALIGN + \
+		 sizeof(struct memarea_objtlr))
+#define MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz) \
+		RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz + \
+			    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))
+#define MEMAREA_SPLIT_OBJECT_MIN_SIZE \
+		(sizeof(struct memarea_objhdr) + MEMAREA_OBJECT_SIZE_ALIGN)
+#define MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz) \
+		RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz)
 #endif
 
 struct memarea_objhdr {
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index ea9067fb35..0c538b54ba 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,8 +2,10 @@
  * Copyright(c) 2023 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_errno.h>
@@ -94,6 +96,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 					init->heap.socket_id);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		ptr = memarea_alloc_from_libc(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		ptr = rte_memarea_alloc(init->ma.src, init->total_sz);
 
 	return ptr;
 }
@@ -105,6 +109,8 @@ memarea_free_area(const struct rte_memarea_param *init, void *ptr)
 		rte_free(ptr);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ptr);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		rte_memarea_free(init->ma.src, ptr);
 }
 
 static inline void
@@ -206,3 +212,156 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(&ma->init, ma->area_base);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+static inline void
+memarea_check_cookie(const struct rte_memarea *ma, const struct memarea_objhdr *hdr, int status)
+{
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	static const char *const str[] = { "PASS", "FAILED" };
+	struct memarea_objtlr *tlr;
+	bool hdr_fail, tlr_fail;
+
+	if (hdr == ma->guard_hdr)
+		return;
+
+	tlr = RTE_PTR_SUB(TAILQ_NEXT(hdr, obj_next), sizeof(struct memarea_objtlr));
+	hdr_fail = (status == COOKIE_EXPECT_STATUS_AVAILABLE &&
+		    hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE) ||
+		   (status == COOKIE_EXPECT_STATUS_ALLOCATED &&
+		    hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE) ||
+		   (status == COOKIE_EXPECT_STATUS_VALID &&
+		    (hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE &&
+		     hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE));
+	tlr_fail = (tlr->cookie != MEMAREA_OBJECT_TRAILER_COOKIE);
+	if (!hdr_fail && !tlr_fail)
+		return;
+
+	rte_panic("MEMAREA: %s check cookies failed! addr-%p header-cookie<0x%" PRIx64 " %s> trailer-cookie<0x%" PRIx64 " %s>\n",
+		ma->init.name, RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr)),
+		hdr->cookie, str[hdr_fail], tlr->cookie, str[tlr_fail]);
+#else
+	RTE_SET_USED(ma);
+	RTE_SET_USED(hdr);
+	RTE_SET_USED(status);
+#endif
+}
+
+static inline void
+memarea_split_object(struct rte_memarea *ma, struct memarea_objhdr *hdr, size_t alloc_sz)
+{
+	struct memarea_objhdr *split_hdr;
+
+	split_hdr = MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz);
+	memarea_set_cookie(split_hdr, COOKIE_TARGET_STATUS_NEW_AVAILABLE);
+	TAILQ_INSERT_AFTER(&ma->obj_list, hdr, split_hdr, obj_next);
+	TAILQ_INSERT_AFTER(&ma->avail_list, hdr, split_hdr, avail_next);
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size)
+{
+	size_t align_sz = RTE_ALIGN(size, MEMAREA_OBJECT_SIZE_ALIGN);
+	struct memarea_objhdr *hdr;
+	size_t avail_sz;
+	void *ptr = NULL;
+
+	if (ma == NULL || size == 0 || align_sz < size) {
+		rte_errno = EINVAL;
+		return ptr;
+	}
+
+	memarea_lock(ma);
+
+	/** traverse every available object, return the first satisfied one. */
+	TAILQ_FOREACH(hdr, &ma->avail_list, avail_next) {
+		/** 1st: check whether the object size meets. */
+		memarea_check_cookie(ma, hdr, COOKIE_EXPECT_STATUS_AVAILABLE);
+		avail_sz = MEMAREA_OBJECT_GET_SIZE(hdr);
+		if (avail_sz < align_sz)
+			continue;
+
+		/** 2nd: if the object size is too long, a new object can be split. */
+		if (avail_sz - align_sz > MEMAREA_SPLIT_OBJECT_MIN_SIZE)
+			memarea_split_object(ma, hdr, align_sz);
+
+		/** 3rd: allocate successful. */
+		TAILQ_REMOVE(&ma->avail_list, hdr, avail_next);
+		MEMAREA_OBJECT_MARK_ALLOCATED(hdr);
+		memarea_set_cookie(hdr, COOKIE_TARGET_STATUS_ALLOCATED);
+
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		break;
+	}
+
+	memarea_unlock(ma);
+
+	if (ptr == NULL)
+		rte_errno = ENOMEM;
+	return ptr;
+}
+
+static inline void
+memarea_merge_object(struct rte_memarea *ma, struct memarea_objhdr *curr,
+		   struct memarea_objhdr *next)
+{
+	RTE_SET_USED(curr);
+	TAILQ_REMOVE(&ma->obj_list, next, obj_next);
+	TAILQ_REMOVE(&ma->avail_list, next, avail_next);
+	memarea_set_cookie(next, COOKIE_TARGET_STATUS_CLEARED);
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	struct memarea_objhdr *hdr, *prev, *next;
+
+	if (ma == NULL || ptr == NULL) {
+		rte_errno = EINVAL;
+		return;
+	}
+
+	hdr = RTE_PTR_SUB(ptr, sizeof(struct memarea_objhdr));
+	if (!MEMAREA_OBJECT_IS_ALLOCATED(hdr)) {
+		RTE_MEMAREA_LOG(ERR, "detect invalid object in %s!", ma->init.name);
+		rte_errno = EFAULT;
+		return;
+	}
+	memarea_check_cookie(ma, hdr, COOKIE_EXPECT_STATUS_ALLOCATED);
+
+	memarea_lock(ma);
+
+	/** 1st: add to avail list. */
+	TAILQ_INSERT_HEAD(&ma->avail_list, hdr, avail_next);
+	memarea_set_cookie(hdr, COOKIE_TARGET_STATUS_AVAILABLE);
+
+	/** 2nd: merge if previous object is avail. */
+	prev = TAILQ_PREV(hdr, memarea_objhdr_list, obj_next);
+	if (prev != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(prev)) {
+		memarea_check_cookie(ma, prev, COOKIE_EXPECT_STATUS_AVAILABLE);
+		memarea_merge_object(ma, prev, hdr);
+		hdr = prev;
+	}
+
+	/** 3rd: merge if next object is avail. */
+	next = TAILQ_NEXT(hdr, obj_next);
+	if (next != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(next)) {
+		memarea_check_cookie(ma, next, COOKIE_EXPECT_STATUS_AVAILABLE);
+		memarea_merge_object(ma, hdr, next);
+	}
+
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 1d4381efd7..bb1bd5bae5 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -134,6 +134,52 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ *
+ * @return
+ *   - NULL on error. Not enough memory, or invalid arguments (see the
+ *     rte_errno).
+ *   - Otherwise, the pointer to the allocated object.
+ *
+ * @note The memory allocated is not guaranteed to be zeroed.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), it must be freed to the same memarea which previous
+ * allocated from. The behaviour of rte_memarea_free() is undefined if the
+ * memarea or pointer does not match these requirements.
+ *
+ * @param ma
+ *   The pointer of memarea. If the ma is NULL, the function does nothing.
+ * @param ptr
+ *   The pointer of memory object which need be freed. If the pointer is NULL,
+ *   the function does nothing.
+ *
+ * @note The rte_errno is set if free failed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..effbd0b488 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,10 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v17 4/6] test/memarea: support alloc and free API test
  2023-07-18  2:40 ` [PATCH v17 0/6] introduce memarea library Chengwen Feng
                     ` (2 preceding siblings ...)
  2023-07-18  2:40   ` [PATCH v17 3/6] memarea: support alloc and free API Chengwen Feng
@ 2023-07-18  2:40   ` Chengwen Feng
  2023-07-18  2:40   ` [PATCH v17 5/6] memarea: support dump API Chengwen Feng
  2023-07-18  2:40   ` [PATCH v17 6/6] test/memarea: support dump API test Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18  2:40 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_alloc() and rte_memarea_free() API
test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 app/test/test_memarea.c | 214 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 213 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index a2191eb0eb..8a1a0e2e2a 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -23,6 +23,12 @@ test_memarea_init_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -95,7 +101,7 @@ test_memarea_create_bad_param(void)
 static int
 test_memarea_create_destroy(void)
 {
-	struct rte_memarea *ma;
+	struct rte_memarea *ma, *src_ma;
 	struct rte_memarea_param init;
 
 	rte_errno = 0;
@@ -115,6 +121,207 @@ test_memarea_create_destroy(void)
 	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with another memarea */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	src_ma = rte_memarea_create(&init);
+	TEST_ASSERT(src_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.ma.src = src_ma;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(src_ma);
+
+	TEST_ASSERT(rte_errno == 0, "Expected ZERO");
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_alloc_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	size_t size;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid ma */
+	ptr = rte_memarea_alloc(NULL, 1);
+	TEST_ASSERT(ptr == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid size (size = 0) */
+	ptr = rte_memarea_alloc(ma, 0);
+	TEST_ASSERT(ptr == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid size (size rewind) */
+	memset(&size, 0xff, sizeof(size));
+	ptr = rte_memarea_alloc(ma, size);
+	TEST_ASSERT(ptr == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_free_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	ptr = rte_memarea_alloc(ma, 1);
+	TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, 1);
+
+	/* test for invalid ma */
+	rte_memarea_free(NULL, ptr);
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid ptr */
+	rte_memarea_free(ma, NULL);
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test repeat free */
+	rte_errno = 0;
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	TEST_ASSERT(rte_errno == 0, "Expected Zero");
+	rte_memarea_free(ma, ptr);
+	TEST_ASSERT(rte_errno == EFAULT, "Expected EFAULT");
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	void *ptr[ALLOC_MAX_NUM];
+	struct rte_memarea *ma;
+	int i;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	rte_errno = 0;
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++) {
+		ptr[i] = rte_memarea_alloc(ma, 1);
+		TEST_ASSERT(ptr[i] != NULL, "Expected Non-NULL");
+		test_memarea_fill_region(ptr[i], 1);
+	}
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	TEST_ASSERT(rte_errno == 0, "Expected Zero");
+
+	rte_memarea_destroy(ma);
+
 	return TEST_SUCCESS;
 }
 
@@ -125,6 +332,11 @@ static struct unit_test_suite memarea_test_suite  = {
 	.unit_test_cases = {
 		TEST_CASE(test_memarea_create_bad_param),
 		TEST_CASE(test_memarea_create_destroy),
+		TEST_CASE(test_memarea_alloc_bad_param),
+		TEST_CASE(test_memarea_free_bad_param),
+		TEST_CASE(test_memarea_alloc_fail),
+		TEST_CASE(test_memarea_free_fail),
+		TEST_CASE(test_memarea_alloc_free),
 
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v17 5/6] memarea: support dump API
  2023-07-18  2:40 ` [PATCH v17 0/6] introduce memarea library Chengwen Feng
                     ` (3 preceding siblings ...)
  2023-07-18  2:40   ` [PATCH v17 4/6] test/memarea: support alloc and free API test Chengwen Feng
@ 2023-07-18  2:40   ` Chengwen Feng
  2023-07-18  2:40   ` [PATCH v17 6/6] test/memarea: support dump API test Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18  2:40 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   3 +
 lib/memarea/rte_memarea.c             | 100 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  21 ++++++
 lib/memarea/version.map               |   1 +
 4 files changed, 125 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 157baf3c7e..ef22294664 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -39,6 +39,9 @@ the memarea.
 The ``rte_memarea_free()`` function is used to free one memory object which
 allocated by ``rte_memarea_alloc()``.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 0c538b54ba..03e196d1b3 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -365,3 +365,103 @@ rte_memarea_free(struct rte_memarea *ma, void *ptr)
 
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_MEMAREA)
+		return "memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_algorithm alg)
+{
+	if (alg == RTE_MEMAREA_ALGORITHM_NEXTFIT)
+		return "nextfit";
+	else
+		return "unknown";
+}
+
+static void
+memarea_dump_objects_brief(struct rte_memarea *ma, FILE *f)
+{
+	uint32_t total_objs = 0, total_avail_objs = 0;
+	struct memarea_objhdr *hdr;
+	size_t total_avail_sz = 0;
+
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, COOKIE_EXPECT_STATUS_VALID);
+		total_objs++;
+		if (!MEMAREA_OBJECT_IS_ALLOCATED(hdr)) {
+			total_avail_objs++;
+			total_avail_sz += MEMAREA_OBJECT_GET_SIZE(hdr);
+		}
+	}
+	fprintf(f, "  total-objects: %u\n", total_objs);
+	fprintf(f, "  total-avail-objects: %u\n", total_avail_objs);
+	fprintf(f, "  total-avail-objects-size: 0x%zx\n", total_avail_sz);
+}
+
+static void
+memarea_dump_objects_detail(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_objhdr *hdr;
+	size_t offset;
+	void *ptr;
+
+	fprintf(f, "  objects:\n");
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, COOKIE_EXPECT_STATUS_VALID);
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		offset = RTE_PTR_DIFF(ptr, ma->area_base);
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+		fprintf(f, "    %p off: 0x%zx size: 0x%zx %s\n",
+			ptr, offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#else
+		fprintf(f, "    off: 0x%zx size: 0x%zx %s\n",
+			offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#endif
+	}
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		fprintf(f, "  heap-numa-socket: %d\n", ma->init.heap.socket_id);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		fprintf(f, "  source-memarea: %s\n", ma->init.ma.src->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	fprintf(f, "  area-base: %p\n", ma->area_base);
+	fprintf(f, "  guard-header: %p\n", ma->guard_hdr);
+#endif
+	memarea_dump_objects_brief(ma, f);
+	if (dump_all)
+		memarea_dump_objects_detail(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index bb1bd5bae5..fa57f6c455 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -180,6 +180,27 @@ void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
 __rte_experimental
 void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned (the rte_errno is set).
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index effbd0b488..9513d91e0b 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 
 	local: *;
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v17 6/6] test/memarea: support dump API test
  2023-07-18  2:40 ` [PATCH v17 0/6] introduce memarea library Chengwen Feng
                     ` (4 preceding siblings ...)
  2023-07-18  2:40   ` [PATCH v17 5/6] memarea: support dump API Chengwen Feng
@ 2023-07-18  2:40   ` Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18  2:40 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_dump() API test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 app/test/test_memarea.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 8a1a0e2e2a..7c751a0d99 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -320,6 +320,45 @@ test_memarea_alloc_free(void)
 
 	TEST_ASSERT(rte_errno == 0, "Expected Zero");
 
+	fprintf(stderr, "There should have no allocated object.\n");
+	rte_memarea_dump(ma, stderr, true);
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	TEST_ASSERT(ret == -1, "Expected -1");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	TEST_ASSERT(ret == -1, "Expected -1");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	fprintf(stderr, "There should have three allocated object.\n");
+	ret = rte_memarea_dump(ma, stderr, true);
+	TEST_ASSERT(ret == 0, "Expected ZERO");
+
 	rte_memarea_destroy(ma);
 
 	return TEST_SUCCESS;
@@ -337,6 +376,7 @@ static struct unit_test_suite memarea_test_suite  = {
 		TEST_CASE(test_memarea_alloc_fail),
 		TEST_CASE(test_memarea_free_fail),
 		TEST_CASE(test_memarea_alloc_free),
+		TEST_CASE(test_memarea_dump),
 
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v16 4/6] test/memarea: support alloc and free API test
  2023-07-17 13:57     ` Burakov, Anatoly
@ 2023-07-18  2:49       ` fengchengwen
  0 siblings, 0 replies; 222+ messages in thread
From: fengchengwen @ 2023-07-18  2:49 UTC (permalink / raw)
  To: Burakov, Anatoly, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

Hi Anatoly,

  Thanks for your review, both fix in v17.

Thanks.

On 2023/7/17 21:57, Burakov, Anatoly wrote:
> On 7/10/2023 7:49 AM, Chengwen Feng wrote:
>> This patch supports rte_memarea_alloc() and rte_memarea_free() API
>> test.
>>
>> 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
>> +test_memarea_free_fail(void)
>> +{
>> +    struct rte_memarea_param init;
>> +    struct rte_memarea *ma;
>> +    void *ptr;
>> +
>> +    test_memarea_init_param(&init);
>> +    init.source = RTE_MEMAREA_SOURCE_LIBC;
>> +    init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
>> +    ma = rte_memarea_create(&init);
>> +    TEST_ASSERT(ma != NULL, "Expected Non-NULL");
>> +
>> +    /* test repeat free */
>> +    rte_errno = 0;
>> +    ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
>> +    TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
>> +    test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
>> +    rte_memarea_free(ma, ptr);
>> +    TEST_ASSERT(rte_errno == 0, "Expected Zero");
>> +    rte_memarea_free(ma, ptr);
>> +    TEST_ASSERT(rte_errno == EFAULT, "Expected EFAULT");
>> +
>> +    rte_memarea_destroy(ma);
>> +
>> +    return 0;
> 
> Same as in other, I think TEST_SUCCESS would be more correct (even though DPDK codebase doesn't always follow that convention).
> 
> Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>

^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v18 0/6] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (21 preceding siblings ...)
  2023-07-18  2:40 ` [PATCH v17 0/6] introduce memarea library Chengwen Feng
@ 2023-07-18 13:46 ` Chengwen Feng
  2023-07-18 13:46   ` [PATCH v18 1/6] memarea: " Chengwen Feng
                     ` (5 more replies)
  2023-07-20  9:22 ` [PATCH v19 0/6] introduce memarea library Chengwen Feng
  23 siblings, 6 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18 13:46 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- 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 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.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Chengwen Feng (6):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc and free API
  test/memarea: support alloc and free API test
  memarea: support dump API
  test/memarea: support dump API test

---
v18:
* v17 reports DPDK CI apply failed, but it maybe the CI self problem 
  because I apply the serial to main and next-net, both are successful,
  so this version just resend v17.
v17:
* address Anatoly's comments: add note and enum for cookie and typo.
v16:
* fix compile failed when clang with thread-safety-analysis
  (by disable annotate_locks in lib/memarea/meson.build).
* add reserved field for 'struct rte_memarea_param' which address
  Stephen's comments.
* fix typo.
v15:
* rebase 23.07
* address Anatoly's mostly comments: use rte_errno, abstract cookie
  helper, more comment about critical function, rename add with split,
  doc limitation.
v14:
* address Stephen's comments: RTE_MEMAREA_LOG use easy impl and add
  __func__ print.
v13:
* address Morten's comments: make debug cookies optional, controlled
  by RTE_LIBRTE_MEMAREA_DEBUG; disabled by default.
* reduce management data overhead.
v12:
* remove rte_memarea_refcnt_update() API which address Dongdong's
  comments.
* refine the variable naming.
* fix some bugs.
v11:
* rebase 23.03
* remove "app/test: add memarea to malloc-perf-autotest" because the
  two algorithm are not comparable which also address previous
  comments.
v10:
* support windows platform.
* add rte_memarea.libc perftest to malloc-perf-autotest.
v9:
* address Dmitry's comments.
* drop features of SOURCE_USER and backup memarea mechanism.
* rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
  to keep with rte_mbuf_refcnt_update name style.
* fix memarea perftest compile failed at windows platform.
* fix spell warning.
v8:
* address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
* small feature patches are combined.
* enhanced backup memory mechanism.
* add memarea to malloc-perf-autotest.
* other tiny naming optimize.
v7:
* repost patches as there are spread over different series in patchwork.
v6:
* address Mattias's comments.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 391 +++++++++++++++++++++
 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  |  57 +++
 doc/guides/rel_notes/release_23_07.rst |   6 +
 lib/memarea/memarea_private.h          | 126 +++++++
 lib/memarea/meson.build                |  12 +
 lib/memarea/rte_memarea.c              | 467 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 208 +++++++++++
 lib/memarea/version.map                |  15 +
 lib/meson.build                        |   1 +
 14 files changed, 1295 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v18 1/6] memarea: introduce memarea library
  2023-07-18 13:46 ` [PATCH v18 0/6] introduce memarea library Chengwen Feng
@ 2023-07-18 13:46   ` Chengwen Feng
  2023-07-18 13:46   ` [PATCH v18 2/6] test/memarea: support memarea test Chengwen Feng
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18 13:46 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

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>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.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  |  48 ++++++
 doc/guides/rel_notes/release_23_07.rst |   6 +
 lib/memarea/memarea_private.h          | 116 ++++++++++++++
 lib/memarea/meson.build                |  12 ++
 lib/memarea/rte_memarea.c              | 208 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 141 +++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 12 files changed, 553 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 5bb8090ebe..9ca665219b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1613,6 +1613,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>
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 d4af85b3ff..4034a9e9c0 100644
--- a/doc/guides/rel_notes/release_23_07.rst
+++ b/doc/guides/rel_notes/release_23_07.rst
@@ -222,6 +222,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..fd485bb7e7
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,116 @@
+/* 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_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
+
+/** Object cookie target status. */
+enum {
+	/** Object is set to be available, but don't set trailer cookie. */
+	COOKIE_TARGET_STATUS_AVAILABLE,
+	/** Object is set to be allocated, but don't set trailer cookie. */
+	COOKIE_TARGET_STATUS_ALLOCATED,
+	/** object is new split, the header cookie will set to be available,
+	 * the trailer cookie of the previous object will be set.
+	 */
+	COOKIE_TARGET_STATUS_NEW_AVAILABLE,
+	/** object is new split, the header cookie will set to be allocated,
+	 * the trailer cookie of the previous object will be set.
+	 */
+	COOKIE_TARGET_STATUS_NEW_ALLOCATED,
+	/** Object is to be merged, it will no longer exist. the header cookie
+	 * is cleared and the trailer cookie of the previous object is cleared.
+	 */
+	COOKIE_TARGET_STATUS_CLEARED,
+};
+
+/** Object cookie expect status. */
+enum {
+	/** Object is supposed to be available. */
+	COOKIE_EXPECT_STATUS_AVAILABLE,
+	/** Object is supposed to be allocated. */
+	COOKIE_EXPECT_STATUS_ALLOCATED,
+	/** Object is supposed to be valid (available or allocated). */
+	COOKIE_EXPECT_STATUS_VALID,
+};
+
+#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_list. */
+	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 trailer 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..48499626fd
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 HiSilicon Limited
+
+sources = files(
+        'rte_memarea.c',
+)
+headers = files(
+        'rte_memarea.h',
+)
+deps += []
+
+annotate_locks = false
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
new file mode 100644
index 0000000000..ea9067fb35
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,208 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <rte_common.h>
+#include <rte_errno.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)
+{
+	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;
+	}
+
+	if (init->reserved_bits != 0 || init->reserved_64s[0] != 0 ||
+	    init->reserved_64s[1] != 0) {
+		RTE_MEMAREA_LOG(ERR, "%s reserved field not zero!", init->name);
+		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);
+}
+
+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, COOKIE_TARGET_STATUS_AVAILABLE);
+
+	/** 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, COOKIE_TARGET_STATUS_NEW_ALLOCATED);
+
+	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..1d4381efd7
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,141 @@
+/* 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 <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_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;
+	/** Reserved for future field, should be initialized to zero. */
+	uint32_t reserved_bits : 31;
+	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;
+	};
+	/** Reserved for future fields, should be initialized to zero. */
+	uint64_t reserved_64s[2];
+};
+
+/**
+ * @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


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v18 2/6] test/memarea: support memarea test
  2023-07-18 13:46 ` [PATCH v18 0/6] introduce memarea library Chengwen Feng
  2023-07-18 13:46   ` [PATCH v18 1/6] memarea: " Chengwen Feng
@ 2023-07-18 13:46   ` Chengwen Feng
  2023-07-18 13:46   ` [PATCH v18 3/6] memarea: support alloc and free API Chengwen Feng
                     ` (3 subsequent siblings)
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18 13:46 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports memarea test of 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>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 139 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 142 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 9ca665219b..b9b9ed2932 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1617,6 +1617,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index b89cf0368f..0d9701d8c6 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -83,6 +83,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -201,6 +202,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..a2191eb0eb
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_errno.h>
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#include "test.h"
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+static void
+test_memarea_init_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "autotest");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for NULL */
+	ma = rte_memarea_create(NULL);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid name */
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid source */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for total_sz */
+	test_memarea_init_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for memarea NULL */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for algorithm invalid */
+	test_memarea_init_param(&init);
+	init.alg = RTE_MEMAREA_ALGORITHM_NEXTFIT + 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for reserved field */
+	test_memarea_init_param(&init);
+	init.reserved_bits = 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	test_memarea_init_param(&init);
+	init.reserved_64s[0] = 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	test_memarea_init_param(&init);
+	init.reserved_64s[1] = 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	struct rte_memarea *ma;
+	struct rte_memarea_param init;
+
+	rte_errno = 0;
+
+	/* test for create with HEAP */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.heap.socket_id = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite memarea_test_suite  = {
+	.suite_name = "Memarea Unit Test Suite",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+		TEST_CASE(test_memarea_create_bad_param),
+		TEST_CASE(test_memarea_create_destroy),
+
+		TEST_CASES_END() /**< NULL terminate unit test array */
+	}
+};
+
+static int
+test_memarea(void)
+{
+	return unit_test_suite_runner(&memarea_test_suite);
+}
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v18 3/6] memarea: support alloc and free API
  2023-07-18 13:46 ` [PATCH v18 0/6] introduce memarea library Chengwen Feng
  2023-07-18 13:46   ` [PATCH v18 1/6] memarea: " Chengwen Feng
  2023-07-18 13:46   ` [PATCH v18 2/6] test/memarea: support memarea test Chengwen Feng
@ 2023-07-18 13:46   ` Chengwen Feng
  2023-07-18 13:46   ` [PATCH v18 4/6] test/memarea: support alloc and free API test Chengwen Feng
                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18 13:46 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_alloc() and rte_memarea_free() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   6 +
 lib/memarea/memarea_private.h         |  10 ++
 lib/memarea/rte_memarea.c             | 159 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  46 ++++++++
 lib/memarea/version.map               |   2 +
 5 files changed, 223 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index bf19090294..157baf3c7e 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,12 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index fd485bb7e7..ab6253294e 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -52,10 +52,20 @@ enum {
 #define MEMAREA_OBJECT_GET_SIZE(hdr) \
 		((uintptr_t)TAILQ_NEXT((hdr), obj_next) - (uintptr_t)(hdr) - \
 		 sizeof(struct memarea_objhdr) - sizeof(struct memarea_objtlr))
+#define MEMAREA_SPLIT_OBJECT_MIN_SIZE \
+		(sizeof(struct memarea_objhdr) + MEMAREA_OBJECT_SIZE_ALIGN + \
+		 sizeof(struct memarea_objtlr))
+#define MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz) \
+		RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz + \
+			    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))
+#define MEMAREA_SPLIT_OBJECT_MIN_SIZE \
+		(sizeof(struct memarea_objhdr) + MEMAREA_OBJECT_SIZE_ALIGN)
+#define MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz) \
+		RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz)
 #endif
 
 struct memarea_objhdr {
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index ea9067fb35..0c538b54ba 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,8 +2,10 @@
  * Copyright(c) 2023 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_errno.h>
@@ -94,6 +96,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 					init->heap.socket_id);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		ptr = memarea_alloc_from_libc(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		ptr = rte_memarea_alloc(init->ma.src, init->total_sz);
 
 	return ptr;
 }
@@ -105,6 +109,8 @@ memarea_free_area(const struct rte_memarea_param *init, void *ptr)
 		rte_free(ptr);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ptr);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		rte_memarea_free(init->ma.src, ptr);
 }
 
 static inline void
@@ -206,3 +212,156 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(&ma->init, ma->area_base);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+static inline void
+memarea_check_cookie(const struct rte_memarea *ma, const struct memarea_objhdr *hdr, int status)
+{
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	static const char *const str[] = { "PASS", "FAILED" };
+	struct memarea_objtlr *tlr;
+	bool hdr_fail, tlr_fail;
+
+	if (hdr == ma->guard_hdr)
+		return;
+
+	tlr = RTE_PTR_SUB(TAILQ_NEXT(hdr, obj_next), sizeof(struct memarea_objtlr));
+	hdr_fail = (status == COOKIE_EXPECT_STATUS_AVAILABLE &&
+		    hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE) ||
+		   (status == COOKIE_EXPECT_STATUS_ALLOCATED &&
+		    hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE) ||
+		   (status == COOKIE_EXPECT_STATUS_VALID &&
+		    (hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE &&
+		     hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE));
+	tlr_fail = (tlr->cookie != MEMAREA_OBJECT_TRAILER_COOKIE);
+	if (!hdr_fail && !tlr_fail)
+		return;
+
+	rte_panic("MEMAREA: %s check cookies failed! addr-%p header-cookie<0x%" PRIx64 " %s> trailer-cookie<0x%" PRIx64 " %s>\n",
+		ma->init.name, RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr)),
+		hdr->cookie, str[hdr_fail], tlr->cookie, str[tlr_fail]);
+#else
+	RTE_SET_USED(ma);
+	RTE_SET_USED(hdr);
+	RTE_SET_USED(status);
+#endif
+}
+
+static inline void
+memarea_split_object(struct rte_memarea *ma, struct memarea_objhdr *hdr, size_t alloc_sz)
+{
+	struct memarea_objhdr *split_hdr;
+
+	split_hdr = MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz);
+	memarea_set_cookie(split_hdr, COOKIE_TARGET_STATUS_NEW_AVAILABLE);
+	TAILQ_INSERT_AFTER(&ma->obj_list, hdr, split_hdr, obj_next);
+	TAILQ_INSERT_AFTER(&ma->avail_list, hdr, split_hdr, avail_next);
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size)
+{
+	size_t align_sz = RTE_ALIGN(size, MEMAREA_OBJECT_SIZE_ALIGN);
+	struct memarea_objhdr *hdr;
+	size_t avail_sz;
+	void *ptr = NULL;
+
+	if (ma == NULL || size == 0 || align_sz < size) {
+		rte_errno = EINVAL;
+		return ptr;
+	}
+
+	memarea_lock(ma);
+
+	/** traverse every available object, return the first satisfied one. */
+	TAILQ_FOREACH(hdr, &ma->avail_list, avail_next) {
+		/** 1st: check whether the object size meets. */
+		memarea_check_cookie(ma, hdr, COOKIE_EXPECT_STATUS_AVAILABLE);
+		avail_sz = MEMAREA_OBJECT_GET_SIZE(hdr);
+		if (avail_sz < align_sz)
+			continue;
+
+		/** 2nd: if the object size is too long, a new object can be split. */
+		if (avail_sz - align_sz > MEMAREA_SPLIT_OBJECT_MIN_SIZE)
+			memarea_split_object(ma, hdr, align_sz);
+
+		/** 3rd: allocate successful. */
+		TAILQ_REMOVE(&ma->avail_list, hdr, avail_next);
+		MEMAREA_OBJECT_MARK_ALLOCATED(hdr);
+		memarea_set_cookie(hdr, COOKIE_TARGET_STATUS_ALLOCATED);
+
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		break;
+	}
+
+	memarea_unlock(ma);
+
+	if (ptr == NULL)
+		rte_errno = ENOMEM;
+	return ptr;
+}
+
+static inline void
+memarea_merge_object(struct rte_memarea *ma, struct memarea_objhdr *curr,
+		   struct memarea_objhdr *next)
+{
+	RTE_SET_USED(curr);
+	TAILQ_REMOVE(&ma->obj_list, next, obj_next);
+	TAILQ_REMOVE(&ma->avail_list, next, avail_next);
+	memarea_set_cookie(next, COOKIE_TARGET_STATUS_CLEARED);
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	struct memarea_objhdr *hdr, *prev, *next;
+
+	if (ma == NULL || ptr == NULL) {
+		rte_errno = EINVAL;
+		return;
+	}
+
+	hdr = RTE_PTR_SUB(ptr, sizeof(struct memarea_objhdr));
+	if (!MEMAREA_OBJECT_IS_ALLOCATED(hdr)) {
+		RTE_MEMAREA_LOG(ERR, "detect invalid object in %s!", ma->init.name);
+		rte_errno = EFAULT;
+		return;
+	}
+	memarea_check_cookie(ma, hdr, COOKIE_EXPECT_STATUS_ALLOCATED);
+
+	memarea_lock(ma);
+
+	/** 1st: add to avail list. */
+	TAILQ_INSERT_HEAD(&ma->avail_list, hdr, avail_next);
+	memarea_set_cookie(hdr, COOKIE_TARGET_STATUS_AVAILABLE);
+
+	/** 2nd: merge if previous object is avail. */
+	prev = TAILQ_PREV(hdr, memarea_objhdr_list, obj_next);
+	if (prev != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(prev)) {
+		memarea_check_cookie(ma, prev, COOKIE_EXPECT_STATUS_AVAILABLE);
+		memarea_merge_object(ma, prev, hdr);
+		hdr = prev;
+	}
+
+	/** 3rd: merge if next object is avail. */
+	next = TAILQ_NEXT(hdr, obj_next);
+	if (next != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(next)) {
+		memarea_check_cookie(ma, next, COOKIE_EXPECT_STATUS_AVAILABLE);
+		memarea_merge_object(ma, hdr, next);
+	}
+
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 1d4381efd7..bb1bd5bae5 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -134,6 +134,52 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ *
+ * @return
+ *   - NULL on error. Not enough memory, or invalid arguments (see the
+ *     rte_errno).
+ *   - Otherwise, the pointer to the allocated object.
+ *
+ * @note The memory allocated is not guaranteed to be zeroed.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), it must be freed to the same memarea which previous
+ * allocated from. The behaviour of rte_memarea_free() is undefined if the
+ * memarea or pointer does not match these requirements.
+ *
+ * @param ma
+ *   The pointer of memarea. If the ma is NULL, the function does nothing.
+ * @param ptr
+ *   The pointer of memory object which need be freed. If the pointer is NULL,
+ *   the function does nothing.
+ *
+ * @note The rte_errno is set if free failed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..effbd0b488 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,10 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v18 4/6] test/memarea: support alloc and free API test
  2023-07-18 13:46 ` [PATCH v18 0/6] introduce memarea library Chengwen Feng
                     ` (2 preceding siblings ...)
  2023-07-18 13:46   ` [PATCH v18 3/6] memarea: support alloc and free API Chengwen Feng
@ 2023-07-18 13:46   ` Chengwen Feng
  2023-07-18 13:46   ` [PATCH v18 5/6] memarea: support dump API Chengwen Feng
  2023-07-18 13:46   ` [PATCH v18 6/6] test/memarea: support dump API test Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18 13:46 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_alloc() and rte_memarea_free() API
test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 app/test/test_memarea.c | 214 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 213 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index a2191eb0eb..8a1a0e2e2a 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -23,6 +23,12 @@ test_memarea_init_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -95,7 +101,7 @@ test_memarea_create_bad_param(void)
 static int
 test_memarea_create_destroy(void)
 {
-	struct rte_memarea *ma;
+	struct rte_memarea *ma, *src_ma;
 	struct rte_memarea_param init;
 
 	rte_errno = 0;
@@ -115,6 +121,207 @@ test_memarea_create_destroy(void)
 	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
 	rte_memarea_destroy(ma);
 
+	/* test for create with another memarea */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	src_ma = rte_memarea_create(&init);
+	TEST_ASSERT(src_ma != NULL, "Expected Non-NULL");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.ma.src = src_ma;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(src_ma);
+
+	TEST_ASSERT(rte_errno == 0, "Expected ZERO");
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_alloc_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	size_t size;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid ma */
+	ptr = rte_memarea_alloc(NULL, 1);
+	TEST_ASSERT(ptr == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid size (size = 0) */
+	ptr = rte_memarea_alloc(ma, 0);
+	TEST_ASSERT(ptr == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid size (size rewind) */
+	memset(&size, 0xff, sizeof(size));
+	ptr = rte_memarea_alloc(ma, size);
+	TEST_ASSERT(ptr == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_free_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	ptr = rte_memarea_alloc(ma, 1);
+	TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, 1);
+
+	/* test for invalid ma */
+	rte_memarea_free(NULL, ptr);
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid ptr */
+	rte_memarea_free(ma, NULL);
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test alloc fail with big size */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	TEST_ASSERT(ptr[0] == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[0] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] == NULL, "Expected NULL");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test repeat free */
+	rte_errno = 0;
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr != NULL, "Expected Non-NULL");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	TEST_ASSERT(rte_errno == 0, "Expected Zero");
+	rte_memarea_free(ma, ptr);
+	TEST_ASSERT(rte_errno == EFAULT, "Expected EFAULT");
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	void *ptr[ALLOC_MAX_NUM];
+	struct rte_memarea *ma;
+	int i;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+	memset(ptr, 0, sizeof(ptr));
+
+	rte_errno = 0;
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++) {
+		ptr[i] = rte_memarea_alloc(ma, 1);
+		TEST_ASSERT(ptr[i] != NULL, "Expected Non-NULL");
+		test_memarea_fill_region(ptr[i], 1);
+	}
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	TEST_ASSERT(rte_errno == 0, "Expected Zero");
+
+	rte_memarea_destroy(ma);
+
 	return TEST_SUCCESS;
 }
 
@@ -125,6 +332,11 @@ static struct unit_test_suite memarea_test_suite  = {
 	.unit_test_cases = {
 		TEST_CASE(test_memarea_create_bad_param),
 		TEST_CASE(test_memarea_create_destroy),
+		TEST_CASE(test_memarea_alloc_bad_param),
+		TEST_CASE(test_memarea_free_bad_param),
+		TEST_CASE(test_memarea_alloc_fail),
+		TEST_CASE(test_memarea_free_fail),
+		TEST_CASE(test_memarea_alloc_free),
 
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v18 5/6] memarea: support dump API
  2023-07-18 13:46 ` [PATCH v18 0/6] introduce memarea library Chengwen Feng
                     ` (3 preceding siblings ...)
  2023-07-18 13:46   ` [PATCH v18 4/6] test/memarea: support alloc and free API test Chengwen Feng
@ 2023-07-18 13:46   ` Chengwen Feng
  2023-07-18 13:46   ` [PATCH v18 6/6] test/memarea: support dump API test Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18 13:46 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   3 +
 lib/memarea/rte_memarea.c             | 100 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  21 ++++++
 lib/memarea/version.map               |   1 +
 4 files changed, 125 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 157baf3c7e..ef22294664 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -39,6 +39,9 @@ the memarea.
 The ``rte_memarea_free()`` function is used to free one memory object which
 allocated by ``rte_memarea_alloc()``.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 0c538b54ba..03e196d1b3 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -365,3 +365,103 @@ rte_memarea_free(struct rte_memarea *ma, void *ptr)
 
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_MEMAREA)
+		return "memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_algorithm alg)
+{
+	if (alg == RTE_MEMAREA_ALGORITHM_NEXTFIT)
+		return "nextfit";
+	else
+		return "unknown";
+}
+
+static void
+memarea_dump_objects_brief(struct rte_memarea *ma, FILE *f)
+{
+	uint32_t total_objs = 0, total_avail_objs = 0;
+	struct memarea_objhdr *hdr;
+	size_t total_avail_sz = 0;
+
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, COOKIE_EXPECT_STATUS_VALID);
+		total_objs++;
+		if (!MEMAREA_OBJECT_IS_ALLOCATED(hdr)) {
+			total_avail_objs++;
+			total_avail_sz += MEMAREA_OBJECT_GET_SIZE(hdr);
+		}
+	}
+	fprintf(f, "  total-objects: %u\n", total_objs);
+	fprintf(f, "  total-avail-objects: %u\n", total_avail_objs);
+	fprintf(f, "  total-avail-objects-size: 0x%zx\n", total_avail_sz);
+}
+
+static void
+memarea_dump_objects_detail(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_objhdr *hdr;
+	size_t offset;
+	void *ptr;
+
+	fprintf(f, "  objects:\n");
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, COOKIE_EXPECT_STATUS_VALID);
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		offset = RTE_PTR_DIFF(ptr, ma->area_base);
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+		fprintf(f, "    %p off: 0x%zx size: 0x%zx %s\n",
+			ptr, offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#else
+		fprintf(f, "    off: 0x%zx size: 0x%zx %s\n",
+			offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#endif
+	}
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		fprintf(f, "  heap-numa-socket: %d\n", ma->init.heap.socket_id);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		fprintf(f, "  source-memarea: %s\n", ma->init.ma.src->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	fprintf(f, "  area-base: %p\n", ma->area_base);
+	fprintf(f, "  guard-header: %p\n", ma->guard_hdr);
+#endif
+	memarea_dump_objects_brief(ma, f);
+	if (dump_all)
+		memarea_dump_objects_detail(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index bb1bd5bae5..fa57f6c455 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -180,6 +180,27 @@ void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
 __rte_experimental
 void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned (the rte_errno is set).
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index effbd0b488..9513d91e0b 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 
 	local: *;
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v18 6/6] test/memarea: support dump API test
  2023-07-18 13:46 ` [PATCH v18 0/6] introduce memarea library Chengwen Feng
                     ` (4 preceding siblings ...)
  2023-07-18 13:46   ` [PATCH v18 5/6] memarea: support dump API Chengwen Feng
@ 2023-07-18 13:46   ` Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-18 13:46 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_dump() API test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 app/test/test_memarea.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 8a1a0e2e2a..7c751a0d99 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -320,6 +320,45 @@ test_memarea_alloc_free(void)
 
 	TEST_ASSERT(rte_errno == 0, "Expected Zero");
 
+	fprintf(stderr, "There should have no allocated object.\n");
+	rte_memarea_dump(ma, stderr, true);
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	int ret;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Expected Non-NULL");
+
+	/* test for invalid parameters */
+	ret = rte_memarea_dump(NULL, stderr, false);
+	TEST_ASSERT(ret == -1, "Expected -1");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	ret = rte_memarea_dump(ma, NULL, false);
+	TEST_ASSERT(ret == -1, "Expected -1");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, 1);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	fprintf(stderr, "There should have three allocated object.\n");
+	ret = rte_memarea_dump(ma, stderr, true);
+	TEST_ASSERT(ret == 0, "Expected ZERO");
+
 	rte_memarea_destroy(ma);
 
 	return TEST_SUCCESS;
@@ -337,6 +376,7 @@ static struct unit_test_suite memarea_test_suite  = {
 		TEST_CASE(test_memarea_alloc_fail),
 		TEST_CASE(test_memarea_free_fail),
 		TEST_CASE(test_memarea_alloc_free),
+		TEST_CASE(test_memarea_dump),
 
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v16 6/6] test/memarea: support dump API test
  2023-07-10  6:49   ` [PATCH v16 6/6] test/memarea: support dump API test Chengwen Feng
@ 2023-07-19 12:09     ` Burakov, Anatoly
  2023-07-20  9:35       ` fengchengwen
  0 siblings, 1 reply; 222+ messages in thread
From: Burakov, Anatoly @ 2023-07-19 12:09 UTC (permalink / raw)
  To: Chengwen Feng, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

On 7/10/2023 7:49 AM, Chengwen Feng wrote:
> This patch supports rte_memarea_dump() API test.
> 
> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
> Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
> Acked-by: Morten Brørup <mb@smartsharesystems.com>
> ---
>   app/test/test_memarea.c | 40 ++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 40 insertions(+)
> 
> diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
> index 4053cdcac9..6511a86699 100644
> --- a/app/test/test_memarea.c
> +++ b/app/test/test_memarea.c
> @@ -320,6 +320,45 @@ test_memarea_alloc_free(void)
>   
>   	TEST_ASSERT(rte_errno == 0, "Expected Zero");
>   
> +	fprintf(stderr, "There should have no allocated object.\n");
> +	rte_memarea_dump(ma, stderr, true);
> +
> +	rte_memarea_destroy(ma);
> +
> +	return 0;
> +}
> +
> +static int
> +test_memarea_dump(void)
> +{
> +	struct rte_memarea_param init;
> +	struct rte_memarea *ma;
> +	int ret;
> +
> +	test_memarea_init_param(&init);
> +	init.source = RTE_MEMAREA_SOURCE_LIBC;
> +	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
> +	ma = rte_memarea_create(&init);
> +	TEST_ASSERT(ma != NULL, "Expected Non-NULL");

Here and in other places: I feel it's better to say *why* we expect 
non-NULL, or make the error message otherwise more meaningful, such as 
"Memarea creation failed".

> +
> +	/* test for invalid parameters */
> +	ret = rte_memarea_dump(NULL, stderr, false);
> +	TEST_ASSERT(ret == -1, "Expected -1");
> +	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
> +	ret = rte_memarea_dump(ma, NULL, false);
> +	TEST_ASSERT(ret == -1, "Expected -1");
> +	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
> +
> +	/* test for dump */
> +	(void)rte_memarea_alloc(ma, 1);
> +	(void)rte_memarea_alloc(ma, 1);
> +	(void)rte_memarea_alloc(ma, 1);
> +	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
> +	(void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
> +	fprintf(stderr, "There should have three allocated object.\n");

I question the value of this printout.

> +	ret = rte_memarea_dump(ma, stderr, true);
> +	TEST_ASSERT(ret == 0, "Expected ZERO");
> +
>   	rte_memarea_destroy(ma);
>   
>   	return 0;
> @@ -337,6 +376,7 @@ static struct unit_test_suite memarea_test_suite  = {
>   		TEST_CASE(test_memarea_alloc_fail),
>   		TEST_CASE(test_memarea_free_fail),
>   		TEST_CASE(test_memarea_alloc_free),
> +		TEST_CASE(test_memarea_dump),
>   
>   		TEST_CASES_END() /**< NULL terminate unit test array */
>   	}

-- 
Thanks,
Anatoly


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v19 0/6] introduce memarea library
  2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
                   ` (22 preceding siblings ...)
  2023-07-18 13:46 ` [PATCH v18 0/6] introduce memarea library Chengwen Feng
@ 2023-07-20  9:22 ` Chengwen Feng
  2023-07-20  9:22   ` [PATCH v19 1/6] memarea: " Chengwen Feng
                     ` (5 more replies)
  23 siblings, 6 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-20  9:22 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

The memarea library is an allocator of variable-size object which based
on a memory region. The main features are as follows:

- 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 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.

Note:
a) The memarea is oriented towards the application layer, which could
provides 'region-based memory management' [1] function.
b) The eal library also provide memory zone/heap management, but these
are tied to huge pages management.

[1] https://en.wikipedia.org/wiki/Region-based_memory_management

Chengwen Feng (6):
  memarea: introduce memarea library
  test/memarea: support memarea test
  memarea: support alloc and free API
  test/memarea: support alloc and free API test
  memarea: support dump API
  test/memarea: support dump API test

---
v19:
* address Anatoly's comments about test_memarea: more meaningful error
  tips, and display really allocated num of dump memarea.
* in that test_memarea, explicitly set rte_errno to zero if test that
  rte_errno is a non-zero value.
* don't support windows platform because CI unit-test always failed with:
  MALLOC_PERTURB_=169 DPDK_TEST=memarea_autotest
v18:
* v17 reports DPDK CI apply failed, but it maybe the CI self problem
  because I apply the serial to main and next-net, both are successful,
  so this version just resend v17.
v17:
* address Anatoly's comments: add note and enum for cookie and typo.
v16:
* fix compile failed when clang with thread-safety-analysis
  (by disable annotate_locks in lib/memarea/meson.build).
* add reserved field for 'struct rte_memarea_param' which address
  Stephen's comments.
* fix typo.
v15:
* rebase 23.07
* address Anatoly's mostly comments: use rte_errno, abstract cookie
  helper, more comment about critical function, rename add with split,
  doc limitation.
v14:
* address Stephen's comments: RTE_MEMAREA_LOG use easy impl and add
  __func__ print.
v13:
* address Morten's comments: make debug cookies optional, controlled
  by RTE_LIBRTE_MEMAREA_DEBUG; disabled by default.
* reduce management data overhead.
v12:
* remove rte_memarea_refcnt_update() API which address Dongdong's
  comments.
* refine the variable naming.
* fix some bugs.
v11:
* rebase 23.03
* remove "app/test: add memarea to malloc-perf-autotest" because the
  two algorithm are not comparable which also address previous
  comments.
v10:
* support windows platform.
* add rte_memarea.libc perftest to malloc-perf-autotest.
v9:
* address Dmitry's comments.
* drop features of SOURCE_USER and backup memarea mechanism.
* rename rte_memarea_update_refcnt to rte_memarea_refcnt_update
  to keep with rte_mbuf_refcnt_update name style.
* fix memarea perftest compile failed at windows platform.
* fix spell warning.
v8:
* address Mattias's comments (rename ALG_DEFAULT with ALG_NEXTFIT).
* small feature patches are combined.
* enhanced backup memory mechanism.
* add memarea to malloc-perf-autotest.
* other tiny naming optimize.
v7:
* repost patches as there are spread over different series in patchwork.
v6:
* address Mattias's comments.
v5:
* fix 09/10 patch spell warning.
v4:
* repost patches as there are spread over different series in patchwork.
v3:
* add memory source of RTE memory.
* add algorithm field to facilitate the introduction of new algorithms.
* fix memarea log don't output problem.
v2:
* fix compile issues reported by dpdk-test-report.
* address Dimitry and Jerin's comments.
* add no MT-safe test.

 MAINTAINERS                            |   6 +
 app/test/meson.build                   |   2 +
 app/test/test_memarea.c                | 438 +++++++++++++++++++++++
 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  |  57 +++
 doc/guides/rel_notes/release_23_07.rst |   6 +
 lib/memarea/memarea_private.h          | 126 +++++++
 lib/memarea/meson.build                |  18 +
 lib/memarea/rte_memarea.c              | 463 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 208 +++++++++++
 lib/memarea/version.map                |  15 +
 lib/meson.build                        |   1 +
 14 files changed, 1344 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_memarea.c
 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

-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v19 1/6] memarea: introduce memarea library
  2023-07-20  9:22 ` [PATCH v19 0/6] introduce memarea library Chengwen Feng
@ 2023-07-20  9:22   ` Chengwen Feng
  2023-07-20  9:22   ` [PATCH v19 2/6] test/memarea: support memarea test Chengwen Feng
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-20  9:22 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

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>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.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  |  48 ++++++
 doc/guides/rel_notes/release_23_07.rst |   6 +
 lib/memarea/memarea_private.h          | 116 ++++++++++++++
 lib/memarea/meson.build                |  18 +++
 lib/memarea/rte_memarea.c              | 204 +++++++++++++++++++++++++
 lib/memarea/rte_memarea.h              | 141 +++++++++++++++++
 lib/memarea/version.map                |  12 ++
 lib/meson.build                        |   1 +
 12 files changed, 555 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 18bc05fccd..bd9cad7ee3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1613,6 +1613,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>
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 6a1c45162b..2751d70740 100644
--- a/doc/guides/rel_notes/release_23_07.rst
+++ b/doc/guides/rel_notes/release_23_07.rst
@@ -222,6 +222,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..fd485bb7e7
--- /dev/null
+++ b/lib/memarea/memarea_private.h
@@ -0,0 +1,116 @@
+/* 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_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
+
+/** Object cookie target status. */
+enum {
+	/** Object is set to be available, but don't set trailer cookie. */
+	COOKIE_TARGET_STATUS_AVAILABLE,
+	/** Object is set to be allocated, but don't set trailer cookie. */
+	COOKIE_TARGET_STATUS_ALLOCATED,
+	/** object is new split, the header cookie will set to be available,
+	 * the trailer cookie of the previous object will be set.
+	 */
+	COOKIE_TARGET_STATUS_NEW_AVAILABLE,
+	/** object is new split, the header cookie will set to be allocated,
+	 * the trailer cookie of the previous object will be set.
+	 */
+	COOKIE_TARGET_STATUS_NEW_ALLOCATED,
+	/** Object is to be merged, it will no longer exist. the header cookie
+	 * is cleared and the trailer cookie of the previous object is cleared.
+	 */
+	COOKIE_TARGET_STATUS_CLEARED,
+};
+
+/** Object cookie expect status. */
+enum {
+	/** Object is supposed to be available. */
+	COOKIE_EXPECT_STATUS_AVAILABLE,
+	/** Object is supposed to be allocated. */
+	COOKIE_EXPECT_STATUS_ALLOCATED,
+	/** Object is supposed to be valid (available or allocated). */
+	COOKIE_EXPECT_STATUS_VALID,
+};
+
+#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_list. */
+	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 trailer 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..7e18c02d3e
--- /dev/null
+++ b/lib/memarea/meson.build
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 HiSilicon Limited
+
+if is_windows
+    build = false
+    reason = 'not supported on Windows'
+    subdir_done()
+endif
+
+sources = files(
+        'rte_memarea.c',
+)
+headers = files(
+        'rte_memarea.h',
+)
+deps += []
+
+annotate_locks = false
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
new file mode 100644
index 0000000000..5d806ca363
--- /dev/null
+++ b/lib/memarea/rte_memarea.c
@@ -0,0 +1,204 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <rte_common.h>
+#include <rte_errno.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)
+{
+	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;
+	}
+
+	if (init->reserved_bits != 0 || init->reserved_64s[0] != 0 ||
+	    init->reserved_64s[1] != 0) {
+		RTE_MEMAREA_LOG(ERR, "%s reserved field not zero!", init->name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void *
+memarea_alloc_from_libc(size_t size)
+{
+	void *ptr = NULL;
+	int ret;
+	ret = posix_memalign(&ptr, RTE_CACHE_LINE_SIZE, size);
+	if (ret != 0)
+		return NULL;
+	return ptr;
+}
+
+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);
+}
+
+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, COOKIE_TARGET_STATUS_AVAILABLE);
+
+	/** 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, COOKIE_TARGET_STATUS_NEW_ALLOCATED);
+
+	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..1d4381efd7
--- /dev/null
+++ b/lib/memarea/rte_memarea.h
@@ -0,0 +1,141 @@
+/* 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 <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_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;
+	/** Reserved for future field, should be initialized to zero. */
+	uint32_t reserved_bits : 31;
+	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;
+	};
+	/** Reserved for future fields, should be initialized to zero. */
+	uint64_t reserved_64s[2];
+};
+
+/**
+ * @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


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v19 2/6] test/memarea: support memarea test
  2023-07-20  9:22 ` [PATCH v19 0/6] introduce memarea library Chengwen Feng
  2023-07-20  9:22   ` [PATCH v19 1/6] memarea: " Chengwen Feng
@ 2023-07-20  9:22   ` Chengwen Feng
  2023-07-20  9:22   ` [PATCH v19 3/6] memarea: support alloc and free API Chengwen Feng
                     ` (3 subsequent siblings)
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-20  9:22 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports memarea test of 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>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 MAINTAINERS             |   1 +
 app/test/meson.build    |   2 +
 app/test/test_memarea.c | 166 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 169 insertions(+)
 create mode 100644 app/test/test_memarea.c

diff --git a/MAINTAINERS b/MAINTAINERS
index bd9cad7ee3..4ee43a9964 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1617,6 +1617,7 @@ Memarea - EXPERIMENTAL
 M: Chengwen Feng <fengchengwen@huawei.com>
 F: lib/memarea
 F: doc/guides/prog_guide/memarea_lib.rst
+F: app/test/test_memarea*
 
 Membership - EXPERIMENTAL
 M: Yipeng Wang <yipeng1.wang@intel.com>
diff --git a/app/test/meson.build b/app/test/meson.build
index b89cf0368f..0d9701d8c6 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -83,6 +83,7 @@ test_sources = files(
         'test_malloc.c',
         'test_malloc_perf.c',
         'test_mbuf.c',
+        'test_memarea.c',
         'test_member.c',
         'test_member_perf.c',
         'test_memcpy.c',
@@ -201,6 +202,7 @@ fast_tests = [
         ['malloc_autotest', false, true],
         ['mbuf_autotest', false, true],
         ['mcslock_autotest', false, true],
+        ['memarea_autotest', true, true],
         ['memcpy_autotest', true, true],
         ['memory_autotest', false, true],
         ['mempool_autotest', false, true],
diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
new file mode 100644
index 0000000000..6078c93a16
--- /dev/null
+++ b/app/test/test_memarea.c
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 HiSilicon Limited
+ */
+
+#ifdef RTE_EXEC_ENV_WINDOWS
+
+#include <stdio.h>
+
+#include "test.h"
+
+static int
+test_memarea(void)
+{
+	printf("memarea not supported on Windows, skipping test\n");
+	return TEST_SKIPPED;
+}
+
+#else
+
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_errno.h>
+#include <rte_memory.h>
+#include <rte_memarea.h>
+
+#include "test.h"
+
+#define MEMAREA_TEST_DEFAULT_SIZE	0x1000
+
+static void
+test_memarea_init_param(struct rte_memarea_param *init)
+{
+	memset(init, 0, sizeof(struct rte_memarea_param));
+	sprintf(init->name, "%s", "autotest");
+	init->source = RTE_MEMAREA_SOURCE_LIBC;
+	init->total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	init->mt_safe = 1;
+}
+
+static int
+test_memarea_create_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+
+	/* test for NULL */
+	rte_errno = 0;
+	ma = rte_memarea_create(NULL);
+	TEST_ASSERT(ma == NULL, "Memarea creation expect fail");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid name */
+	rte_errno = 0;
+	memset(&init, 0, sizeof(init));
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Memarea creation expect fail");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	rte_errno = 0;
+	memset(&init.name, 1, sizeof(init.name));
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Memarea creation expect fail");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid source */
+	rte_errno = 0;
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA + 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Memarea creation expect fail");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for total_sz */
+	rte_errno = 0;
+	test_memarea_init_param(&init);
+	init.total_sz = 0;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Memarea creation expect fail");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for memarea NULL */
+	rte_errno = 0;
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Memarea creation expect fail");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for algorithm invalid */
+	rte_errno = 0;
+	test_memarea_init_param(&init);
+	init.alg = RTE_MEMAREA_ALGORITHM_NEXTFIT + 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Memarea creation expect fail");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for reserved field */
+	rte_errno = 0;
+	test_memarea_init_param(&init);
+	init.reserved_bits = 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Memarea creation expect fail");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	rte_errno = 0;
+	test_memarea_init_param(&init);
+	init.reserved_64s[0] = 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Memarea creation expect fail");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	rte_errno = 0;
+	test_memarea_init_param(&init);
+	init.reserved_64s[1] = 1;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma == NULL, "Memarea creation expect fail");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_create_destroy(void)
+{
+	struct rte_memarea *ma;
+	struct rte_memarea_param init;
+
+	rte_errno = 0;
+
+	/* test for create with HEAP */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_HEAP;
+	init.heap.socket_id = SOCKET_ID_ANY;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Memarea creation failed");
+	rte_memarea_destroy(ma);
+
+	/* test for create with LIBC */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Memarea creation failed");
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite memarea_test_suite  = {
+	.suite_name = "Memarea Unit Test Suite",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+		TEST_CASE(test_memarea_create_bad_param),
+		TEST_CASE(test_memarea_create_destroy),
+
+		TEST_CASES_END() /**< NULL terminate unit test array */
+	}
+};
+
+static int
+test_memarea(void)
+{
+	return unit_test_suite_runner(&memarea_test_suite);
+}
+
+#endif /* RTE_EXEC_ENV_WINDOWS */
+
+REGISTER_TEST_COMMAND(memarea_autotest, test_memarea);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v19 3/6] memarea: support alloc and free API
  2023-07-20  9:22 ` [PATCH v19 0/6] introduce memarea library Chengwen Feng
  2023-07-20  9:22   ` [PATCH v19 1/6] memarea: " Chengwen Feng
  2023-07-20  9:22   ` [PATCH v19 2/6] test/memarea: support memarea test Chengwen Feng
@ 2023-07-20  9:22   ` Chengwen Feng
  2023-07-20  9:22   ` [PATCH v19 4/6] test/memarea: support alloc and free API test Chengwen Feng
                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-20  9:22 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_alloc() and rte_memarea_free() API.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   6 +
 lib/memarea/memarea_private.h         |  10 ++
 lib/memarea/rte_memarea.c             | 159 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  46 ++++++++
 lib/memarea/version.map               |   2 +
 5 files changed, 223 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index bf19090294..157baf3c7e 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -33,6 +33,12 @@ returns the pointer to the created memarea or ``NULL`` if the creation failed.
 
 The ``rte_memarea_destroy()`` function is used to destroy a memarea.
 
+The ``rte_memarea_alloc()`` function is used to alloc one memory object from
+the memarea.
+
+The ``rte_memarea_free()`` function is used to free one memory object which
+allocated by ``rte_memarea_alloc()``.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/memarea_private.h b/lib/memarea/memarea_private.h
index fd485bb7e7..ab6253294e 100644
--- a/lib/memarea/memarea_private.h
+++ b/lib/memarea/memarea_private.h
@@ -52,10 +52,20 @@ enum {
 #define MEMAREA_OBJECT_GET_SIZE(hdr) \
 		((uintptr_t)TAILQ_NEXT((hdr), obj_next) - (uintptr_t)(hdr) - \
 		 sizeof(struct memarea_objhdr) - sizeof(struct memarea_objtlr))
+#define MEMAREA_SPLIT_OBJECT_MIN_SIZE \
+		(sizeof(struct memarea_objhdr) + MEMAREA_OBJECT_SIZE_ALIGN + \
+		 sizeof(struct memarea_objtlr))
+#define MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz) \
+		RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz + \
+			    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))
+#define MEMAREA_SPLIT_OBJECT_MIN_SIZE \
+		(sizeof(struct memarea_objhdr) + MEMAREA_OBJECT_SIZE_ALIGN)
+#define MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz) \
+		RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr) + alloc_sz)
 #endif
 
 struct memarea_objhdr {
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 5d806ca363..7a35c875a7 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -2,8 +2,10 @@
  * Copyright(c) 2023 HiSilicon Limited
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/queue.h>
 
 #include <rte_common.h>
 #include <rte_errno.h>
@@ -90,6 +92,8 @@ memarea_alloc_area(const struct rte_memarea_param *init)
 					init->heap.socket_id);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		ptr = memarea_alloc_from_libc(init->total_sz);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		ptr = rte_memarea_alloc(init->ma.src, init->total_sz);
 
 	return ptr;
 }
@@ -101,6 +105,8 @@ memarea_free_area(const struct rte_memarea_param *init, void *ptr)
 		rte_free(ptr);
 	else if (init->source == RTE_MEMAREA_SOURCE_LIBC)
 		free(ptr);
+	else if (init->source == RTE_MEMAREA_SOURCE_MEMAREA)
+		rte_memarea_free(init->ma.src, ptr);
 }
 
 static inline void
@@ -202,3 +208,156 @@ rte_memarea_destroy(struct rte_memarea *ma)
 	memarea_free_area(&ma->init, ma->area_base);
 	rte_free(ma);
 }
+
+static inline void
+memarea_lock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_lock(&ma->lock);
+}
+
+static inline void
+memarea_unlock(struct rte_memarea *ma)
+{
+	if (ma->init.mt_safe)
+		rte_spinlock_unlock(&ma->lock);
+}
+
+static inline void
+memarea_check_cookie(const struct rte_memarea *ma, const struct memarea_objhdr *hdr, int status)
+{
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	static const char *const str[] = { "PASS", "FAILED" };
+	struct memarea_objtlr *tlr;
+	bool hdr_fail, tlr_fail;
+
+	if (hdr == ma->guard_hdr)
+		return;
+
+	tlr = RTE_PTR_SUB(TAILQ_NEXT(hdr, obj_next), sizeof(struct memarea_objtlr));
+	hdr_fail = (status == COOKIE_EXPECT_STATUS_AVAILABLE &&
+		    hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE) ||
+		   (status == COOKIE_EXPECT_STATUS_ALLOCATED &&
+		    hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE) ||
+		   (status == COOKIE_EXPECT_STATUS_VALID &&
+		    (hdr->cookie != MEMAREA_OBJECT_HEADER_AVAILABLE_COOKIE &&
+		     hdr->cookie != MEMAREA_OBJECT_HEADER_ALLOCATED_COOKIE));
+	tlr_fail = (tlr->cookie != MEMAREA_OBJECT_TRAILER_COOKIE);
+	if (!hdr_fail && !tlr_fail)
+		return;
+
+	rte_panic("MEMAREA: %s check cookies failed! addr-%p header-cookie<0x%" PRIx64 " %s> trailer-cookie<0x%" PRIx64 " %s>\n",
+		ma->init.name, RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr)),
+		hdr->cookie, str[hdr_fail], tlr->cookie, str[tlr_fail]);
+#else
+	RTE_SET_USED(ma);
+	RTE_SET_USED(hdr);
+	RTE_SET_USED(status);
+#endif
+}
+
+static inline void
+memarea_split_object(struct rte_memarea *ma, struct memarea_objhdr *hdr, size_t alloc_sz)
+{
+	struct memarea_objhdr *split_hdr;
+
+	split_hdr = MEMAREA_SPLIT_OBJECT_GET_HEADER(hdr, alloc_sz);
+	memarea_set_cookie(split_hdr, COOKIE_TARGET_STATUS_NEW_AVAILABLE);
+	TAILQ_INSERT_AFTER(&ma->obj_list, hdr, split_hdr, obj_next);
+	TAILQ_INSERT_AFTER(&ma->avail_list, hdr, split_hdr, avail_next);
+}
+
+void *
+rte_memarea_alloc(struct rte_memarea *ma, size_t size)
+{
+	size_t align_sz = RTE_ALIGN(size, MEMAREA_OBJECT_SIZE_ALIGN);
+	struct memarea_objhdr *hdr;
+	size_t avail_sz;
+	void *ptr = NULL;
+
+	if (ma == NULL || size == 0 || align_sz < size) {
+		rte_errno = EINVAL;
+		return ptr;
+	}
+
+	memarea_lock(ma);
+
+	/** traverse every available object, return the first satisfied one. */
+	TAILQ_FOREACH(hdr, &ma->avail_list, avail_next) {
+		/** 1st: check whether the object size meets. */
+		memarea_check_cookie(ma, hdr, COOKIE_EXPECT_STATUS_AVAILABLE);
+		avail_sz = MEMAREA_OBJECT_GET_SIZE(hdr);
+		if (avail_sz < align_sz)
+			continue;
+
+		/** 2nd: if the object size is too long, a new object can be split. */
+		if (avail_sz - align_sz > MEMAREA_SPLIT_OBJECT_MIN_SIZE)
+			memarea_split_object(ma, hdr, align_sz);
+
+		/** 3rd: allocate successful. */
+		TAILQ_REMOVE(&ma->avail_list, hdr, avail_next);
+		MEMAREA_OBJECT_MARK_ALLOCATED(hdr);
+		memarea_set_cookie(hdr, COOKIE_TARGET_STATUS_ALLOCATED);
+
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		break;
+	}
+
+	memarea_unlock(ma);
+
+	if (ptr == NULL)
+		rte_errno = ENOMEM;
+	return ptr;
+}
+
+static inline void
+memarea_merge_object(struct rte_memarea *ma, struct memarea_objhdr *curr,
+		   struct memarea_objhdr *next)
+{
+	RTE_SET_USED(curr);
+	TAILQ_REMOVE(&ma->obj_list, next, obj_next);
+	TAILQ_REMOVE(&ma->avail_list, next, avail_next);
+	memarea_set_cookie(next, COOKIE_TARGET_STATUS_CLEARED);
+}
+
+void
+rte_memarea_free(struct rte_memarea *ma, void *ptr)
+{
+	struct memarea_objhdr *hdr, *prev, *next;
+
+	if (ma == NULL || ptr == NULL) {
+		rte_errno = EINVAL;
+		return;
+	}
+
+	hdr = RTE_PTR_SUB(ptr, sizeof(struct memarea_objhdr));
+	if (!MEMAREA_OBJECT_IS_ALLOCATED(hdr)) {
+		RTE_MEMAREA_LOG(ERR, "detect invalid object in %s!", ma->init.name);
+		rte_errno = EFAULT;
+		return;
+	}
+	memarea_check_cookie(ma, hdr, COOKIE_EXPECT_STATUS_ALLOCATED);
+
+	memarea_lock(ma);
+
+	/** 1st: add to avail list. */
+	TAILQ_INSERT_HEAD(&ma->avail_list, hdr, avail_next);
+	memarea_set_cookie(hdr, COOKIE_TARGET_STATUS_AVAILABLE);
+
+	/** 2nd: merge if previous object is avail. */
+	prev = TAILQ_PREV(hdr, memarea_objhdr_list, obj_next);
+	if (prev != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(prev)) {
+		memarea_check_cookie(ma, prev, COOKIE_EXPECT_STATUS_AVAILABLE);
+		memarea_merge_object(ma, prev, hdr);
+		hdr = prev;
+	}
+
+	/** 3rd: merge if next object is avail. */
+	next = TAILQ_NEXT(hdr, obj_next);
+	if (next != NULL && !MEMAREA_OBJECT_IS_ALLOCATED(next)) {
+		memarea_check_cookie(ma, next, COOKIE_EXPECT_STATUS_AVAILABLE);
+		memarea_merge_object(ma, hdr, next);
+	}
+
+	memarea_unlock(ma);
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index 1d4381efd7..bb1bd5bae5 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -134,6 +134,52 @@ struct rte_memarea *rte_memarea_create(const struct rte_memarea_param *init);
 __rte_experimental
 void rte_memarea_destroy(struct rte_memarea *ma);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate memory from memarea.
+ *
+ * Allocate one memory object from the memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param size
+ *   The memory size to be allocated.
+ *
+ * @return
+ *   - NULL on error. Not enough memory, or invalid arguments (see the
+ *     rte_errno).
+ *   - Otherwise, the pointer to the allocated object.
+ *
+ * @note The memory allocated is not guaranteed to be zeroed.
+ */
+__rte_experimental
+void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Free memory to memarea.
+ *
+ * Free one memory object to the memarea.
+ * @note The memory object must have been returned by a previous call to
+ * rte_memarea_alloc(), it must be freed to the same memarea which previous
+ * allocated from. The behaviour of rte_memarea_free() is undefined if the
+ * memarea or pointer does not match these requirements.
+ *
+ * @param ma
+ *   The pointer of memarea. If the ma is NULL, the function does nothing.
+ * @param ptr
+ *   The pointer of memory object which need be freed. If the pointer is NULL,
+ *   the function does nothing.
+ *
+ * @note The rte_errno is set if free failed.
+ */
+__rte_experimental
+void rte_memarea_free(struct rte_memarea *ma, void *ptr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index f36a04d7cf..effbd0b488 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -1,8 +1,10 @@
 EXPERIMENTAL {
 	global:
 
+	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_free;
 
 	local: *;
 };
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v19 4/6] test/memarea: support alloc and free API test
  2023-07-20  9:22 ` [PATCH v19 0/6] introduce memarea library Chengwen Feng
                     ` (2 preceding siblings ...)
  2023-07-20  9:22   ` [PATCH v19 3/6] memarea: support alloc and free API Chengwen Feng
@ 2023-07-20  9:22   ` Chengwen Feng
  2023-07-20  9:22   ` [PATCH v19 5/6] memarea: support dump API Chengwen Feng
  2023-07-20  9:22   ` [PATCH v19 6/6] test/memarea: support dump API test Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-20  9:22 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_alloc() and rte_memarea_free() API
test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 app/test/test_memarea.c | 222 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 221 insertions(+), 1 deletion(-)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 6078c93a16..805fb82d08 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -38,6 +38,12 @@ test_memarea_init_param(struct rte_memarea_param *init)
 	init->mt_safe = 1;
 }
 
+static void
+test_memarea_fill_region(void *ptr, size_t size)
+{
+	memset(ptr, 0xff, size);
+}
+
 static int
 test_memarea_create_bad_param(void)
 {
@@ -120,7 +126,7 @@ test_memarea_create_bad_param(void)
 static int
 test_memarea_create_destroy(void)
 {
-	struct rte_memarea *ma;
+	struct rte_memarea *ma, *src_ma;
 	struct rte_memarea_param init;
 
 	rte_errno = 0;
@@ -140,6 +146,215 @@ test_memarea_create_destroy(void)
 	TEST_ASSERT(ma != NULL, "Memarea creation failed");
 	rte_memarea_destroy(ma);
 
+	/* test for create with another memarea */
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	src_ma = rte_memarea_create(&init);
+	TEST_ASSERT(src_ma != NULL, "Memarea creation failed");
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_MEMAREA;
+	init.total_sz = init.total_sz >> 1;
+	init.ma.src = src_ma;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Memarea creation failed");
+	rte_memarea_destroy(ma);
+	rte_memarea_destroy(src_ma);
+
+	TEST_ASSERT(rte_errno == 0, "Expected ZERO");
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_alloc_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	size_t size;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Memarea creation failed");
+
+	/* test for invalid ma */
+	rte_errno = 0;
+	ptr = rte_memarea_alloc(NULL, 1);
+	TEST_ASSERT(ptr == NULL, "Memarea allocation expect fail");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid size (size = 0) */
+	rte_errno = 0;
+	ptr = rte_memarea_alloc(ma, 0);
+	TEST_ASSERT(ptr == NULL, "Memarea allocation expect fail");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid size (size rewind) */
+	rte_errno = 0;
+	memset(&size, 0xff, sizeof(size));
+	ptr = rte_memarea_alloc(ma, size);
+	TEST_ASSERT(ptr == NULL, "Memarea allocation expect fail");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_free_bad_param(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Memarea creation failed");
+	ptr = rte_memarea_alloc(ma, 1);
+	TEST_ASSERT(ptr != NULL, "Memarea allocation failed");
+	test_memarea_fill_region(ptr, 1);
+
+	/* test for invalid ma */
+	rte_errno = 0;
+	rte_memarea_free(NULL, ptr);
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for invalid ptr */
+	rte_errno = 0;
+	rte_memarea_free(ma, NULL);
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_alloc_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr[2];
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Memarea creation failed");
+
+	/* test alloc fail with big size */
+	rte_errno = 0;
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	TEST_ASSERT(ptr[0] == NULL, "Memarea allocation expect fail");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+
+	/* test alloc fail because no memory */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[0] != NULL, "Memarea allocation failed");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_errno = 0;
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] == NULL, "Memarea allocation expect fail");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+	rte_memarea_free(ma, ptr[0]);
+
+	/* test alloc fail when second fail */
+	ptr[0] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[0] != NULL, "Memarea allocation failed");
+	test_memarea_fill_region(ptr[0], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_errno = 0;
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] == NULL, "Memarea allocation expect fail");
+	TEST_ASSERT(rte_errno == ENOMEM, "Expected ENOMEM");
+	rte_memarea_free(ma, ptr[0]);
+	ptr[1] = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr[1] != NULL, "Memarea allocation failed");
+	test_memarea_fill_region(ptr[1], MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr[1]);
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_free_fail(void)
+{
+	struct rte_memarea_param init;
+	struct rte_memarea *ma;
+	void *ptr;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Memarea creation failed");
+
+	/* test repeat free */
+	rte_errno = 0;
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	TEST_ASSERT(ptr != NULL, "Memarea allocation failed");
+	test_memarea_fill_region(ptr, MEMAREA_TEST_DEFAULT_SIZE >> 1);
+	rte_memarea_free(ma, ptr);
+	TEST_ASSERT(rte_errno == 0, "Expected Zero");
+	rte_memarea_free(ma, ptr);
+	TEST_ASSERT(rte_errno == EFAULT, "Expected EFAULT");
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_alloc_free(void)
+{
+#define ALLOC_MAX_NUM	8
+	struct rte_memarea_param init;
+	void *ptr[ALLOC_MAX_NUM];
+	struct rte_memarea *ma;
+	int i;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Memarea creation failed");
+	memset(ptr, 0, sizeof(ptr));
+
+	rte_errno = 0;
+
+	/* test random alloc and free */
+	for (i = 0; i < ALLOC_MAX_NUM; i++) {
+		ptr[i] = rte_memarea_alloc(ma, 1);
+		TEST_ASSERT(ptr[i] != NULL, "Memarea allocation failed");
+		test_memarea_fill_region(ptr[i], 1);
+	}
+
+	/* test merge left */
+	rte_memarea_free(ma, ptr[0]);
+	rte_memarea_free(ma, ptr[1]);
+
+	/* test merge right */
+	rte_memarea_free(ma, ptr[7]);
+	rte_memarea_free(ma, ptr[6]);
+
+	/* test merge left and right */
+	rte_memarea_free(ma, ptr[3]);
+	rte_memarea_free(ma, ptr[2]);
+
+	/* test merge remains */
+	rte_memarea_free(ma, ptr[4]);
+	rte_memarea_free(ma, ptr[5]);
+
+	TEST_ASSERT(rte_errno == 0, "Expected Zero");
+
+	rte_memarea_destroy(ma);
+
 	return TEST_SUCCESS;
 }
 
@@ -150,6 +365,11 @@ static struct unit_test_suite memarea_test_suite  = {
 	.unit_test_cases = {
 		TEST_CASE(test_memarea_create_bad_param),
 		TEST_CASE(test_memarea_create_destroy),
+		TEST_CASE(test_memarea_alloc_bad_param),
+		TEST_CASE(test_memarea_free_bad_param),
+		TEST_CASE(test_memarea_alloc_fail),
+		TEST_CASE(test_memarea_free_fail),
+		TEST_CASE(test_memarea_alloc_free),
 
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v19 5/6] memarea: support dump API
  2023-07-20  9:22 ` [PATCH v19 0/6] introduce memarea library Chengwen Feng
                     ` (3 preceding siblings ...)
  2023-07-20  9:22   ` [PATCH v19 4/6] test/memarea: support alloc and free API test Chengwen Feng
@ 2023-07-20  9:22   ` Chengwen Feng
  2023-07-20  9:22   ` [PATCH v19 6/6] test/memarea: support dump API test Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-20  9:22 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_dump() API which could be used for
debug.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
Acked-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
 doc/guides/prog_guide/memarea_lib.rst |   3 +
 lib/memarea/rte_memarea.c             | 100 ++++++++++++++++++++++++++
 lib/memarea/rte_memarea.h             |  21 ++++++
 lib/memarea/version.map               |   1 +
 4 files changed, 125 insertions(+)

diff --git a/doc/guides/prog_guide/memarea_lib.rst b/doc/guides/prog_guide/memarea_lib.rst
index 157baf3c7e..ef22294664 100644
--- a/doc/guides/prog_guide/memarea_lib.rst
+++ b/doc/guides/prog_guide/memarea_lib.rst
@@ -39,6 +39,9 @@ the memarea.
 The ``rte_memarea_free()`` function is used to free one memory object which
 allocated by ``rte_memarea_alloc()``.
 
++The ``rte_memarea_dump()`` function is used to dump the internal information
++of a memarea.
+
 Debug Mode
 ----------
 
diff --git a/lib/memarea/rte_memarea.c b/lib/memarea/rte_memarea.c
index 7a35c875a7..d5d9a46736 100644
--- a/lib/memarea/rte_memarea.c
+++ b/lib/memarea/rte_memarea.c
@@ -361,3 +361,103 @@ rte_memarea_free(struct rte_memarea *ma, void *ptr)
 
 	memarea_unlock(ma);
 }
+
+static const char *
+memarea_source_name(enum rte_memarea_source source)
+{
+	if (source == RTE_MEMAREA_SOURCE_HEAP)
+		return "heap";
+	else if (source == RTE_MEMAREA_SOURCE_LIBC)
+		return "libc";
+	else if (source == RTE_MEMAREA_SOURCE_MEMAREA)
+		return "memarea";
+	else
+		return "unknown";
+}
+
+static const char *
+memarea_alg_name(enum rte_memarea_algorithm alg)
+{
+	if (alg == RTE_MEMAREA_ALGORITHM_NEXTFIT)
+		return "nextfit";
+	else
+		return "unknown";
+}
+
+static void
+memarea_dump_objects_brief(struct rte_memarea *ma, FILE *f)
+{
+	uint32_t total_objs = 0, total_avail_objs = 0;
+	struct memarea_objhdr *hdr;
+	size_t total_avail_sz = 0;
+
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, COOKIE_EXPECT_STATUS_VALID);
+		total_objs++;
+		if (!MEMAREA_OBJECT_IS_ALLOCATED(hdr)) {
+			total_avail_objs++;
+			total_avail_sz += MEMAREA_OBJECT_GET_SIZE(hdr);
+		}
+	}
+	fprintf(f, "  total-objects: %u\n", total_objs);
+	fprintf(f, "  total-avail-objects: %u\n", total_avail_objs);
+	fprintf(f, "  total-avail-objects-size: 0x%zx\n", total_avail_sz);
+}
+
+static void
+memarea_dump_objects_detail(struct rte_memarea *ma, FILE *f)
+{
+	struct memarea_objhdr *hdr;
+	size_t offset;
+	void *ptr;
+
+	fprintf(f, "  objects:\n");
+	TAILQ_FOREACH(hdr, &ma->obj_list, obj_next) {
+		if (hdr == ma->guard_hdr)
+			break;
+		memarea_check_cookie(ma, hdr, COOKIE_EXPECT_STATUS_VALID);
+		ptr = RTE_PTR_ADD(hdr, sizeof(struct memarea_objhdr));
+		offset = RTE_PTR_DIFF(ptr, ma->area_base);
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+		fprintf(f, "    %p off: 0x%zx size: 0x%zx %s\n",
+			ptr, offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#else
+		fprintf(f, "    off: 0x%zx size: 0x%zx %s\n",
+			offset, MEMAREA_OBJECT_GET_SIZE(hdr),
+			MEMAREA_OBJECT_IS_ALLOCATED(hdr) ? "allocated" : "");
+#endif
+	}
+}
+
+int
+rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all)
+{
+	if (ma == NULL || f == NULL) {
+		rte_errno = EINVAL;
+		return -1;
+	}
+
+	memarea_lock(ma);
+	fprintf(f, "memarea name: %s\n", ma->init.name);
+	fprintf(f, "  source: %s\n", memarea_source_name(ma->init.source));
+	if (ma->init.source == RTE_MEMAREA_SOURCE_HEAP)
+		fprintf(f, "  heap-numa-socket: %d\n", ma->init.heap.socket_id);
+	else if (ma->init.source == RTE_MEMAREA_SOURCE_MEMAREA)
+		fprintf(f, "  source-memarea: %s\n", ma->init.ma.src->init.name);
+	fprintf(f, "  algorithm: %s\n", memarea_alg_name(ma->init.alg));
+	fprintf(f, "  total-size: 0x%zx\n", ma->init.total_sz);
+	fprintf(f, "  mt-safe: %s\n", ma->init.mt_safe ? "yes" : "no");
+#ifdef RTE_LIBRTE_MEMAREA_DEBUG
+	fprintf(f, "  area-base: %p\n", ma->area_base);
+	fprintf(f, "  guard-header: %p\n", ma->guard_hdr);
+#endif
+	memarea_dump_objects_brief(ma, f);
+	if (dump_all)
+		memarea_dump_objects_detail(ma, f);
+	memarea_unlock(ma);
+
+	return 0;
+}
diff --git a/lib/memarea/rte_memarea.h b/lib/memarea/rte_memarea.h
index bb1bd5bae5..fa57f6c455 100644
--- a/lib/memarea/rte_memarea.h
+++ b/lib/memarea/rte_memarea.h
@@ -180,6 +180,27 @@ void *rte_memarea_alloc(struct rte_memarea *ma, size_t size);
 __rte_experimental
 void rte_memarea_free(struct rte_memarea *ma, void *ptr);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Dump memarea.
+ *
+ * Dump one memarea.
+ *
+ * @param ma
+ *   The pointer of memarea.
+ * @param f
+ *   The file to write the output to.
+ * @param dump_all
+ *   Indicate whether to dump the allocated and free memory objects information.
+ *
+ * @return
+ *   0 on success. Otherwise negative value is returned (the rte_errno is set).
+ */
+__rte_experimental
+int rte_memarea_dump(struct rte_memarea *ma, FILE *f, bool dump_all);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/memarea/version.map b/lib/memarea/version.map
index effbd0b488..9513d91e0b 100644
--- a/lib/memarea/version.map
+++ b/lib/memarea/version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	rte_memarea_alloc;
 	rte_memarea_create;
 	rte_memarea_destroy;
+	rte_memarea_dump;
 	rte_memarea_free;
 
 	local: *;
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* [PATCH v19 6/6] test/memarea: support dump API test
  2023-07-20  9:22 ` [PATCH v19 0/6] introduce memarea library Chengwen Feng
                     ` (4 preceding siblings ...)
  2023-07-20  9:22   ` [PATCH v19 5/6] memarea: support dump API Chengwen Feng
@ 2023-07-20  9:22   ` Chengwen Feng
  5 siblings, 0 replies; 222+ messages in thread
From: Chengwen Feng @ 2023-07-20  9:22 UTC (permalink / raw)
  To: thomas, david.marchand
  Cc: dev, mb, anatoly.burakov, dmitry.kozliuk, jerinjacobk, hofors, stephen

This patch supports rte_memarea_dump() API test.

Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
Acked-by: Morten Brørup <mb@smartsharesystems.com>
---
 app/test/test_memarea.c | 52 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
index 805fb82d08..e793022aa2 100644
--- a/app/test/test_memarea.c
+++ b/app/test/test_memarea.c
@@ -353,6 +353,57 @@ test_memarea_alloc_free(void)
 
 	TEST_ASSERT(rte_errno == 0, "Expected Zero");
 
+	fprintf(stderr, "There should have no allocated object.\n");
+	rte_memarea_dump(ma, stderr, true);
+
+	rte_memarea_destroy(ma);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_memarea_dump(void)
+{
+	struct rte_memarea_param init;
+	uint32_t alloced_num = 0;
+	struct rte_memarea *ma;
+	void *ptr;
+	int ret;
+
+	test_memarea_init_param(&init);
+	init.source = RTE_MEMAREA_SOURCE_LIBC;
+	init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
+	ma = rte_memarea_create(&init);
+	TEST_ASSERT(ma != NULL, "Memarea creation failed");
+
+	/* test for invalid parameters */
+	rte_errno = 0;
+	ret = rte_memarea_dump(NULL, stderr, false);
+	TEST_ASSERT(ret == -1, "Expected -1");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+	rte_errno = 0;
+	ret = rte_memarea_dump(ma, NULL, false);
+	TEST_ASSERT(ret == -1, "Expected -1");
+	TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
+
+	/* test for dump */
+	ptr = rte_memarea_alloc(ma, 1);
+	TEST_ASSERT(ptr != NULL, "Memarea allocation failed");
+	alloced_num++;
+	ptr = rte_memarea_alloc(ma, 1);
+	TEST_ASSERT(ptr != NULL, "Memarea allocation failed");
+	alloced_num++;
+	ptr = rte_memarea_alloc(ma, 1);
+	TEST_ASSERT(ptr != NULL, "Memarea allocation failed");
+	alloced_num++;
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	TEST_ASSERT(ptr == NULL, "Memarea allocation expect fail");
+	ptr = rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
+	TEST_ASSERT(ptr == NULL, "Memarea allocation expect fail");
+	fprintf(stderr, "There should have %u allocated object.\n", alloced_num);
+	ret = rte_memarea_dump(ma, stderr, true);
+	TEST_ASSERT(ret == 0, "Memarea dump failed");
+
 	rte_memarea_destroy(ma);
 
 	return TEST_SUCCESS;
@@ -370,6 +421,7 @@ static struct unit_test_suite memarea_test_suite  = {
 		TEST_CASE(test_memarea_alloc_fail),
 		TEST_CASE(test_memarea_free_fail),
 		TEST_CASE(test_memarea_alloc_free),
+		TEST_CASE(test_memarea_dump),
 
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
-- 
2.17.1


^ permalink raw reply	[flat|nested] 222+ messages in thread

* Re: [PATCH v16 6/6] test/memarea: support dump API test
  2023-07-19 12:09     ` Burakov, Anatoly
@ 2023-07-20  9:35       ` fengchengwen
  0 siblings, 0 replies; 222+ messages in thread
From: fengchengwen @ 2023-07-20  9:35 UTC (permalink / raw)
  To: Burakov, Anatoly, thomas, david.marchand
  Cc: dev, mb, dmitry.kozliuk, jerinjacobk, hofors, stephen

Hi Anatoly,

On 2023/7/19 20:09, Burakov, Anatoly wrote:
> On 7/10/2023 7:49 AM, Chengwen Feng wrote:
>> This patch supports rte_memarea_dump() API test.
>>
>> Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
>> Reviewed-by: Dongdong Liu <liudongdong3@huawei.com>
>> Acked-by: Morten Brørup <mb@smartsharesystems.com>
>> ---
>>   app/test/test_memarea.c | 40 ++++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 40 insertions(+)
>>
>> diff --git a/app/test/test_memarea.c b/app/test/test_memarea.c
>> index 4053cdcac9..6511a86699 100644
>> --- a/app/test/test_memarea.c
>> +++ b/app/test/test_memarea.c
>> @@ -320,6 +320,45 @@ test_memarea_alloc_free(void)
>>         TEST_ASSERT(rte_errno == 0, "Expected Zero");
>>   +    fprintf(stderr, "There should have no allocated object.\n");
>> +    rte_memarea_dump(ma, stderr, true);
>> +
>> +    rte_memarea_destroy(ma);
>> +
>> +    return 0;
>> +}
>> +
>> +static int
>> +test_memarea_dump(void)
>> +{
>> +    struct rte_memarea_param init;
>> +    struct rte_memarea *ma;
>> +    int ret;
>> +
>> +    test_memarea_init_param(&init);
>> +    init.source = RTE_MEMAREA_SOURCE_LIBC;
>> +    init.total_sz = MEMAREA_TEST_DEFAULT_SIZE;
>> +    ma = rte_memarea_create(&init);
>> +    TEST_ASSERT(ma != NULL, "Expected Non-NULL");
> 
> Here and in other places: I feel it's better to say *why* we expect non-NULL, or make the error message otherwise more meaningful, such as "Memarea creation failed".

It already fix in v19.

> 
>> +
>> +    /* test for invalid parameters */
>> +    ret = rte_memarea_dump(NULL, stderr, false);
>> +    TEST_ASSERT(ret == -1, "Expected -1");
>> +    TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
>> +    ret = rte_memarea_dump(ma, NULL, false);
>> +    TEST_ASSERT(ret == -1, "Expected -1");
>> +    TEST_ASSERT(rte_errno == EINVAL, "Expected EINVAL");
>> +
>> +    /* test for dump */
>> +    (void)rte_memarea_alloc(ma, 1);
>> +    (void)rte_memarea_alloc(ma, 1);
>> +    (void)rte_memarea_alloc(ma, 1);
>> +    (void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
>> +    (void)rte_memarea_alloc(ma, MEMAREA_TEST_DEFAULT_SIZE);
>> +    fprintf(stderr, "There should have three allocated object.\n");
> 
> I question the value of this printout.

I have change the implemention (in v19): so it will depend on the rte_memarea_alloc result.

> 
>> +    ret = rte_memarea_dump(ma, stderr, true);
>> +    TEST_ASSERT(ret == 0, "Expected ZERO");
>> +
>>       rte_memarea_destroy(ma);
>>         return 0;
>> @@ -337,6 +376,7 @@ static struct unit_test_suite memarea_test_suite  = {
>>           TEST_CASE(test_memarea_alloc_fail),
>>           TEST_CASE(test_memarea_free_fail),
>>           TEST_CASE(test_memarea_alloc_free),
>> +        TEST_CASE(test_memarea_dump),
>>             TEST_CASES_END() /**< NULL terminate unit test array */
>>       }
> 

Thanks.

^ permalink raw reply	[flat|nested] 222+ messages in thread

end of thread, other threads:[~2023-07-20  9:35 UTC | newest]

Thread overview: 222+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-21  4:46 [RFC] memarea: introduce memory area library Chengwen Feng
2022-07-21 15:22 ` Stephen Hemminger
2022-08-04  6:36 ` Jerin Jacob
2022-08-30 12:41 ` Dmitry Kozlyuk
2022-09-20  3:46 ` [PATCH 0/8] introduce memarea library Chengwen Feng
2022-09-20  3:46   ` [PATCH 1/8] memarea: introduce memory area library Chengwen Feng
2022-09-20 11:30     ` Dmitry Kozlyuk
2022-09-20 11:31       ` Dmitry Kozlyuk
2022-09-20 12:06       ` fengchengwen
2022-09-20  3:46   ` [PATCH 2/8] test/memarea: support memarea test Chengwen Feng
2022-09-20  3:46   ` [PATCH 3/8] memarea: support alloc/free/update-refcnt API Chengwen Feng
2022-09-20  3:46   ` [PATCH 4/8] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
2022-09-20  3:46   ` [PATCH 5/8] memarea: support dump API Chengwen Feng
2022-09-20  3:46   ` [PATCH 6/8] test/memarea: support dump test Chengwen Feng
2022-09-20  3:46   ` [PATCH 7/8] memarea: support backup memory mechanism Chengwen Feng
2022-09-20  3:46   ` [PATCH 8/8] test/memarea: support backup memory test Chengwen Feng
2022-09-21  3:12 ` [PATCH v2 0/9] introduce memarea library Chengwen Feng
2022-09-21  3:12   ` [PATCH v2 1/9] memarea: introduce memory area library Chengwen Feng
2022-09-21  3:12   ` [PATCH v2 2/9] test/memarea: support memarea test Chengwen Feng
2022-09-21  3:12   ` [PATCH v2 3/9] memarea: support alloc/free/update-refcnt API Chengwen Feng
2022-09-21  3:12   ` [PATCH v2 4/9] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
2022-09-21  3:12   ` [PATCH v2 5/9] memarea: support dump API Chengwen Feng
2022-09-21  3:12   ` [PATCH v2 6/9] test/memarea: support dump test Chengwen Feng
2022-09-21  3:12   ` [PATCH v2 7/9] memarea: support backup memory mechanism Chengwen Feng
2022-09-21  3:12   ` [PATCH v2 8/9] test/memarea: support backup memory test Chengwen Feng
2022-09-21  3:12   ` [PATCH v2 9/9] test/memarea: support no MT-safe test Chengwen Feng
2022-09-24  7:49 ` [PATCH v3 00/10] introduce memarea library Chengwen Feng
2022-09-24  7:49   ` [PATCH v3 01/10] memarea: " Chengwen Feng
2022-09-24  7:49   ` [PATCH v3 02/10] test/memarea: support memarea test Chengwen Feng
2022-09-24  7:49   ` [PATCH v3 03/10] memarea: support alloc/free/update-refcnt API Chengwen Feng
2022-09-24  7:49   ` [PATCH v3 04/10] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
2022-09-24  7:49   ` [PATCH v3 05/10] memarea: support dump API Chengwen Feng
2022-09-24  7:49   ` [PATCH v3 06/10] test/memarea: support dump test Chengwen Feng
2022-09-24  7:49   ` [PATCH v3 07/10] memarea: support backup memory mechanism Chengwen Feng
2022-09-24  7:49   ` [PATCH v3 08/10] test/memarea: support backup memory test Chengwen Feng
2022-09-24  7:49   ` [PATCH v3 09/10] memarea: detect memory corruption based on magic Chengwen Feng
2022-09-24  7:49   ` [PATCH v3 10/10] test/memarea: support no MT-safe test Chengwen Feng
2022-10-03  7:42   ` [PATCH v3 00/10] introduce memarea library David Marchand
2022-10-05  4:19     ` datshan
2022-10-05  3:38 ` [PATCH v4 " datshan
     [not found] ` <20221005033848.2241-1-datshan@qq.com>
2022-10-05  3:38   ` [PATCH v4 01/10] memarea: " datshan
2022-10-05  3:38   ` [PATCH v4 02/10] test/memarea: support memarea test datshan
2022-10-05  3:38   ` [PATCH v4 03/10] memarea: support alloc/free/update-refcnt API datshan
2022-10-05  3:38   ` [PATCH v4 04/10] test/memarea: support alloc/free/update-refcnt test datshan
2022-10-05  3:38   ` [PATCH v4 05/10] memarea: support dump API datshan
2022-10-05  3:38   ` [PATCH v4 06/10] test/memarea: support dump test datshan
2022-10-05  3:38   ` [PATCH v4 07/10] memarea: support backup memory mechanism datshan
2022-10-05  3:38   ` [PATCH v4 08/10] test/memarea: support backup memory test datshan
2022-10-05  3:38   ` [PATCH v4 09/10] memarea: detect memory corruption based on magic datshan
2022-10-05  3:38   ` [PATCH v4 10/10] test/memarea: support no MT-safe test datshan
2022-10-05  4:09 ` [PATCH v5 00/10] introduce memarea library datshan
     [not found] ` <20221005040952.8166-1-datshan@qq.com>
2022-10-05  4:09   ` [PATCH v5 01/10] memarea: " datshan
2022-10-06 20:15     ` Mattias Rönnblom
2022-10-08  7:53       ` fengchengwen
2022-10-10 16:53         ` Mattias Rönnblom
2022-10-10 23:33           ` fengchengwen
2022-10-11 15:35             ` Mattias Rönnblom
2022-10-05  4:09   ` [PATCH v5 02/10] test/memarea: support memarea test datshan
2022-10-05  4:09   ` [PATCH v5 03/10] memarea: support alloc/free/update-refcnt API datshan
2022-10-06 19:43     ` Mattias Rönnblom
2022-10-08  8:02       ` fengchengwen
2022-10-05  4:09   ` [PATCH v5 04/10] test/memarea: support alloc/free/update-refcnt test datshan
2022-10-05  4:09   ` [PATCH v5 05/10] memarea: support dump API datshan
2022-10-05  4:09   ` [PATCH v5 06/10] test/memarea: support dump test datshan
2022-10-05  4:09   ` [PATCH v5 07/10] memarea: support backup memory mechanism datshan
2022-10-06 19:53     ` Mattias Rönnblom
2022-10-08  7:56       ` fengchengwen
2022-10-05  4:09   ` [PATCH v5 08/10] test/memarea: support backup memory test datshan
2022-10-05  4:09   ` [PATCH v5 09/10] memarea: detect memory corruption based on magic datshan
2022-10-05  4:09   ` [PATCH v5 10/10] test/memarea: support no MT-safe test datshan
2022-10-08 11:33 ` [PATCH v6 00/10] introduce memarea library Chengwen Feng
2022-10-08 11:33   ` [PATCH v6 01/10] memarea: " Chengwen Feng
2022-10-08 11:33   ` [PATCH v6 02/10] test/memarea: support memarea test Chengwen Feng
2022-10-08 11:33   ` [PATCH v6 03/10] memarea: support alloc/free/update-refcnt API Chengwen Feng
2022-10-08 11:33   ` [PATCH v6 04/10] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
2022-10-08 11:33   ` [PATCH v6 05/10] memarea: support dump API Chengwen Feng
2022-10-08 11:33   ` [PATCH v6 06/10] test/memarea: support dump test Chengwen Feng
2022-10-08 11:33   ` [PATCH v6 07/10] memarea: support backup memory mechanism Chengwen Feng
2022-10-08 11:33   ` [PATCH v6 08/10] test/memarea: support backup memory test Chengwen Feng
2022-10-08 11:33   ` [PATCH v6 09/10] memarea: detect memory corruption based on magic Chengwen Feng
2022-10-08 11:33   ` [PATCH v6 10/10] test/memarea: support no MT-safe test Chengwen Feng
2022-10-08 11:58 ` [PATCH v7 00/10] introduce memarea library Chengwen Feng
2022-10-08 11:58   ` [PATCH v7 01/10] memarea: " Chengwen Feng
2022-10-08 11:58   ` [PATCH v7 02/10] test/memarea: support memarea test Chengwen Feng
2022-10-08 11:58   ` [PATCH v7 03/10] memarea: support alloc/free/update-refcnt API Chengwen Feng
2022-10-08 11:58   ` [PATCH v7 04/10] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
2022-10-08 11:58   ` [PATCH v7 05/10] memarea: support dump API Chengwen Feng
2022-10-08 11:58   ` [PATCH v7 06/10] test/memarea: support dump test Chengwen Feng
2022-10-08 11:58   ` [PATCH v7 07/10] memarea: support backup memory mechanism Chengwen Feng
2022-10-08 11:58   ` [PATCH v7 08/10] test/memarea: support backup memory test Chengwen Feng
2022-10-08 11:58   ` [PATCH v7 09/10] memarea: detect memory corruption based on magic Chengwen Feng
2022-10-08 11:58   ` [PATCH v7 10/10] test/memarea: support no MT-safe test Chengwen Feng
2022-10-08 12:14   ` [PATCH v7 00/10] introduce memarea library fengchengwen
2022-10-11 12:17 ` [PATCH v8 0/9] " Chengwen Feng
2022-10-11 12:17   ` [PATCH v8 1/9] memarea: " Chengwen Feng
2022-10-11 15:58     ` Dmitry Kozlyuk
2022-10-12  4:06       ` fengchengwen
2022-10-13 10:45         ` fengchengwen
2022-10-11 12:17   ` [PATCH v8 2/9] test/memarea: support memarea test Chengwen Feng
2022-10-11 12:17   ` [PATCH v8 3/9] memarea: support alloc/free/update-refcnt API Chengwen Feng
2022-10-11 15:58     ` Dmitry Kozlyuk
2022-10-12  7:28       ` fengchengwen
2022-10-11 12:17   ` [PATCH v8 4/9] test/memarea: support alloc/free/update-refcnt test Chengwen Feng
2022-10-11 12:17   ` [PATCH v8 5/9] memarea: support dump API Chengwen Feng
2022-10-11 12:17   ` [PATCH v8 6/9] test/memarea: support dump test Chengwen Feng
2022-10-11 12:17   ` [PATCH v8 7/9] memarea: support backup memory mechanism Chengwen Feng
2022-10-11 15:58     ` Dmitry Kozlyuk
2022-10-11 20:26       ` Mattias Rönnblom
2022-10-12  8:23         ` fengchengwen
2022-10-12  7:57       ` fengchengwen
2022-10-11 12:17   ` [PATCH v8 8/9] test/memarea: support backup memory test Chengwen Feng
2022-10-11 12:17   ` [PATCH v8 9/9] app/test: add memarea to malloc-perf-autotest Chengwen Feng
2022-10-11 15:58     ` Dmitry Kozlyuk
2022-10-12  8:03       ` fengchengwen
2022-10-12 10:47 ` [PATCH v9 0/7] introduce memarea library Chengwen Feng
2022-10-12 10:47   ` [PATCH v9 1/7] memarea: " Chengwen Feng
2022-10-12 10:48   ` [PATCH v9 2/7] test/memarea: support memarea test Chengwen Feng
2022-10-12 10:48   ` [PATCH v9 3/7] memarea: support alloc/free/refcnt-update API Chengwen Feng
2022-10-12 10:48   ` [PATCH v9 4/7] test/memarea: support alloc/free/refcnt-update test Chengwen Feng
2022-10-12 10:48   ` [PATCH v9 5/7] memarea: support dump API Chengwen Feng
2022-10-12 10:48   ` [PATCH v9 6/7] test/memarea: support dump test Chengwen Feng
2022-10-12 10:48   ` [PATCH v9 7/7] app/test: add memarea to malloc-perf-autotest Chengwen Feng
2022-10-17  3:40 ` [PATCH v10 0/7] introduce memarea library Chengwen Feng
2022-10-17  3:40   ` [PATCH v10 1/7] memarea: " Chengwen Feng
2022-10-17  3:40   ` [PATCH v10 2/7] test/memarea: support memarea test Chengwen Feng
2022-10-17  3:40   ` [PATCH v10 3/7] memarea: support alloc/free/refcnt-update API Chengwen Feng
2022-10-17  3:40   ` [PATCH v10 4/7] test/memarea: support alloc/free/refcnt-update test Chengwen Feng
2022-10-17  3:40   ` [PATCH v10 5/7] memarea: support dump API Chengwen Feng
2022-10-17  3:40   ` [PATCH v10 6/7] test/memarea: support dump test Chengwen Feng
2022-10-17  3:40   ` [PATCH v10 7/7] app/test: add memarea to malloc-perf-autotest Chengwen Feng
2022-12-13  9:13 ` [PATCH v11 0/6] introduce memarea library Chengwen Feng
2022-12-13  9:13   ` [PATCH v11 1/6] memarea: " Chengwen Feng
2022-12-13  9:13   ` [PATCH v11 2/6] test/memarea: support memarea test Chengwen Feng
2022-12-13  9:13   ` [PATCH v11 3/6] memarea: support alloc/free/refcnt-update API Chengwen Feng
2023-01-10  2:16     ` Dongdong Liu
2022-12-13  9:13   ` [PATCH v11 4/6] test/memarea: support alloc/free/refcnt-update test Chengwen Feng
2022-12-13  9:13   ` [PATCH v11 5/6] memarea: support dump API Chengwen Feng
2022-12-13  9:13   ` [PATCH v11 6/6] test/memarea: support dump test Chengwen Feng
2023-01-10  2:21   ` [PATCH v11 0/6] introduce memarea library Dongdong Liu
2023-01-14 11:49 ` [PATCH v12 " Chengwen Feng
2023-01-14 11:49   ` [PATCH v12 1/6] memarea: " Chengwen Feng
2023-01-15  7:58     ` Morten Brørup
2023-01-20  8:20       ` fengchengwen
2023-01-20  9:05         ` Morten Brørup
2023-01-14 11:49   ` [PATCH v12 2/6] test/memarea: support memarea test Chengwen Feng
2023-01-14 11:49   ` [PATCH v12 3/6] memarea: support alloc and free API Chengwen Feng
2023-01-14 11:49   ` [PATCH v12 4/6] test/memarea: support alloc and free API test Chengwen Feng
2023-01-14 11:49   ` [PATCH v12 5/6] memarea: support dump API Chengwen Feng
2023-01-14 11:49   ` [PATCH v12 6/6] test/memarea: support dump API test Chengwen Feng
2023-02-08  8:24 ` [PATCH v13 0/6] introduce memarea library Chengwen Feng
2023-02-08  8:24   ` [PATCH v13 1/6] memarea: " Chengwen Feng
2023-02-09  0:04     ` Stephen Hemminger
2023-02-09  6:48       ` fengchengwen
2023-02-08  8:24   ` [PATCH v13 2/6] test/memarea: support memarea test Chengwen Feng
2023-02-08  8:24   ` [PATCH v13 3/6] memarea: support alloc and free API Chengwen Feng
2023-02-08  8:24   ` [PATCH v13 4/6] test/memarea: support alloc and free API test Chengwen Feng
2023-02-08  8:24   ` [PATCH v13 5/6] memarea: support dump API Chengwen Feng
2023-02-08  8:24   ` [PATCH v13 6/6] test/memarea: support dump API test Chengwen Feng
2023-02-08  8:43   ` [PATCH v13 0/6] introduce memarea library Morten Brørup
2023-02-09  6:36 ` [PATCH v14 " Chengwen Feng
2023-02-09  6:36   ` [PATCH v14 1/6] memarea: " Chengwen Feng
2023-06-21 10:52     ` Burakov, Anatoly
2023-06-21 12:05     ` Burakov, Anatoly
2023-02-09  6:36   ` [PATCH v14 2/6] test/memarea: support memarea test Chengwen Feng
2023-06-21 11:00     ` Burakov, Anatoly
2023-02-09  6:36   ` [PATCH v14 3/6] memarea: support alloc and free API Chengwen Feng
2023-06-22 15:29     ` Burakov, Anatoly
2023-02-09  6:36   ` [PATCH v14 4/6] test/memarea: support alloc and free API test Chengwen Feng
2023-02-09  6:36   ` [PATCH v14 5/6] memarea: support dump API Chengwen Feng
2023-06-22 15:55     ` Burakov, Anatoly
2023-06-28  1:25       ` fengchengwen
2023-06-28  1:39         ` Thomas Monjalon
2023-06-28  1:48           ` fengchengwen
2023-07-03 10:29           ` Burakov, Anatoly
2023-02-09  6:36   ` [PATCH v14 6/6] test/memarea: support dump API test Chengwen Feng
2023-06-12 13:53   ` [PATCH v14 0/6] introduce memarea library Ferruh Yigit
2023-06-13  9:50     ` fengchengwen
2023-06-13 11:04       ` Burakov, Anatoly
2023-06-13 12:46         ` fengchengwen
2023-07-09 13:00 ` [PATCH v15 " Chengwen Feng
2023-07-09 13:00   ` [PATCH v15 1/6] memarea: " Chengwen Feng
2023-07-09 19:46     ` Stephen Hemminger
2023-07-09 13:00   ` [PATCH v15 2/6] test/memarea: support memarea test Chengwen Feng
2023-07-09 13:00   ` [PATCH v15 3/6] memarea: support alloc and free API Chengwen Feng
2023-07-09 13:00   ` [PATCH v15 4/6] test/memarea: support alloc and free API test Chengwen Feng
2023-07-09 13:00   ` [PATCH v15 5/6] memarea: support dump API Chengwen Feng
2023-07-09 13:00   ` [PATCH v15 6/6] test/memarea: support dump API test Chengwen Feng
2023-07-10  6:49 ` [PATCH v16 0/6] introduce memarea library Chengwen Feng
2023-07-10  6:49   ` [PATCH v16 1/6] memarea: " Chengwen Feng
2023-07-17 13:19     ` Burakov, Anatoly
2023-07-10  6:49   ` [PATCH v16 2/6] test/memarea: support memarea test Chengwen Feng
2023-07-17 13:22     ` Burakov, Anatoly
2023-07-10  6:49   ` [PATCH v16 3/6] memarea: support alloc and free API Chengwen Feng
2023-07-17 13:33     ` Burakov, Anatoly
2023-07-10  6:49   ` [PATCH v16 4/6] test/memarea: support alloc and free API test Chengwen Feng
2023-07-17 13:57     ` Burakov, Anatoly
2023-07-18  2:49       ` fengchengwen
2023-07-10  6:49   ` [PATCH v16 5/6] memarea: support dump API Chengwen Feng
2023-07-10  6:49   ` [PATCH v16 6/6] test/memarea: support dump API test Chengwen Feng
2023-07-19 12:09     ` Burakov, Anatoly
2023-07-20  9:35       ` fengchengwen
2023-07-18  2:40 ` [PATCH v17 0/6] introduce memarea library Chengwen Feng
2023-07-18  2:40   ` [PATCH v17 1/6] memarea: " Chengwen Feng
2023-07-18  2:40   ` [PATCH v17 2/6] test/memarea: support memarea test Chengwen Feng
2023-07-18  2:40   ` [PATCH v17 3/6] memarea: support alloc and free API Chengwen Feng
2023-07-18  2:40   ` [PATCH v17 4/6] test/memarea: support alloc and free API test Chengwen Feng
2023-07-18  2:40   ` [PATCH v17 5/6] memarea: support dump API Chengwen Feng
2023-07-18  2:40   ` [PATCH v17 6/6] test/memarea: support dump API test Chengwen Feng
2023-07-18 13:46 ` [PATCH v18 0/6] introduce memarea library Chengwen Feng
2023-07-18 13:46   ` [PATCH v18 1/6] memarea: " Chengwen Feng
2023-07-18 13:46   ` [PATCH v18 2/6] test/memarea: support memarea test Chengwen Feng
2023-07-18 13:46   ` [PATCH v18 3/6] memarea: support alloc and free API Chengwen Feng
2023-07-18 13:46   ` [PATCH v18 4/6] test/memarea: support alloc and free API test Chengwen Feng
2023-07-18 13:46   ` [PATCH v18 5/6] memarea: support dump API Chengwen Feng
2023-07-18 13:46   ` [PATCH v18 6/6] test/memarea: support dump API test Chengwen Feng
2023-07-20  9:22 ` [PATCH v19 0/6] introduce memarea library Chengwen Feng
2023-07-20  9:22   ` [PATCH v19 1/6] memarea: " Chengwen Feng
2023-07-20  9:22   ` [PATCH v19 2/6] test/memarea: support memarea test Chengwen Feng
2023-07-20  9:22   ` [PATCH v19 3/6] memarea: support alloc and free API Chengwen Feng
2023-07-20  9:22   ` [PATCH v19 4/6] test/memarea: support alloc and free API test Chengwen Feng
2023-07-20  9:22   ` [PATCH v19 5/6] memarea: support dump API Chengwen Feng
2023-07-20  9:22   ` [PATCH v19 6/6] test/memarea: support dump API test Chengwen Feng

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).